#define TEST
#define NO_TRY
#undef NO_TRY

#region copyright notice
/* ====================================================================
 * The UncleGED Open Source Software License
 *
 * Copyright (c) 1999-2009 D. M. DeBacker. All rights reserved.
 * http://gatheringleaves.org/uged
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. Products derived from this software may not be called "UncleGED",
 *    nor may "UncleGED" appear in their name, without prior written
 *    permission of D. M. DeBacker.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL D. M. DEBACKER BE LIABLE FOR ANY 
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 */
#endregion


#region Bugs and fixes
//FIX: add an option use either ".html" or ".htm"
//FIX: don't display last updated if the date is "Last Updated: 01 January 0001"
#endregion

using System;
using System.Collections;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Collections.Generic;
using UncleGED.Documentation;
using UncleGED.Utils;
using UncleGED.Application;
using UncleGED.Collections;
using log4net;
using System.Reflection;

namespace UncleGED
{
	#region Event Handler
	/// <summary>
	/// Represents the method that will handle the Status Message event
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An EventArgs that contains the event data</param>
	/// <tocexclude/>
	public delegate void StatusMessageEventHandler(object sender, StatusMessageArgs e);

	/// <summary>
	/// Status Message Event Arguments
	/// </summary>
	/// <tocexclude/>
	public class StatusMessageArgs : EventArgs
	{
		/// <summary>
		/// Text of the message
		/// </summary>
		public string Message;
		/// <summary>
		/// Used to set the Maximum value of progress bars when tracking progress messages
		/// </summary>
		public long CountTotal;
		/// <summary>
		/// Used to sent the Value of  progress bars when tracking progress status messages
		/// </summary>
		public long Counter;
		/// <summary>
		/// If true the message is a progress message
		/// </summary>
		public bool IsProgress;
		/// <summary>
		/// If true the message is an error message
		/// </summary>
		public bool IsError = false;
		/// <summary>
		/// If true the message is a debug message
		/// </summary>
		public bool IsDebug = false;

		/// <summary>
		/// Constructor for progress status message event arguments
		/// </summary>
		/// <param name="MessageString">Status message text</param>
		/// <param name="TotalProgessMessages">Current total number of progress messages</param>
		/// <param name="MessageCount">Current counter number of the progress message</param>
		/// <param name="isProgress">Flag indicating that this a progress message</param>
		public StatusMessageArgs(string MessageString, long TotalProgessMessages, long MessageCount, bool isProgress)
		{
			Message = MessageString;
			CountTotal = TotalProgessMessages;
			Counter = MessageCount;
			IsProgress = isProgress;
		}

		/// <summary>
		/// Constructor for error status message event arguments
		/// </summary>
		/// <param name="MessageString">Status message text</param>
		/// <param name="TotalProgessMessages">Current total number of progress messages</param>
		/// <param name="MessageCount">Current counter number of the progress message</param>
		/// <param name="isProgress">Flag indicating that this a progress message</param>
		/// <param name="isError">Flag indicating that this an error message</param>
		public StatusMessageArgs(string MessageString, long TotalProgessMessages, long MessageCount, bool isProgress, bool isError)
		{
			Message = MessageString;
			CountTotal = TotalProgessMessages;
			Counter = MessageCount;
			IsProgress = isProgress;
			IsError = isError;
		}

		/// <summary>
		/// Constructor for error status message event arguments
		/// </summary>
		/// <param name="MessageString">Status message text</param>
		/// <param name="isDebug">Flag indicating that this an debug message</param>
		public StatusMessageArgs(string MessageString, bool isDebug)
		{
			Message = MessageString;
			CountTotal = 0;
			Counter = 0;
			IsProgress = false;
			IsError = false;
			IsDebug = isDebug;
		}
	}
	#endregion


	/// <summary>
	/// The GedReader class is used by Uncle to process the GEDCOM and transform the data into HTML pages.
	/// </summary>
	/// <tocexclude/>
	public class GedReader
	{
		#region events
		/// <summary>
		/// Event fired to send status message to the application
		/// </summary>
		public event StatusMessageEventHandler StatusMessage;

		/// <summary>
		/// Fires a status event
		/// </summary>
		private void FireStatusMessage(string message)
		{
			if (StatusMessage != null)
			{
				StatusMessageArgs e = new StatusMessageArgs(message, 0, 0, false);
				StatusMessage(this, e);
			}
		}

		/// <summary>
		/// Fires a error status event
		/// </summary>
		private void FireErrorMessage(string message)
		{
			if (StatusMessage != null)
			{
				StatusMessageArgs e = new StatusMessageArgs(message, 0, 0, false, true);
				StatusMessage(this, e);
			}
		}

		/// <summary>
		/// Fires a debug status event
		/// </summary>
		private void FireDebugMessage(string message)
		{
			if (StatusMessage != null)
			{
				StatusMessageArgs e = new StatusMessageArgs(message, true);
				StatusMessage(this, e);
			}
		}

		/// <summary>
		/// Fires a progress status event
		/// </summary>
		private void FireProgressMessage(string message, long cntTotal, long counter)
		{
			if (StatusMessage != null)
			{
				StatusMessageArgs e = new StatusMessageArgs(message, cntTotal, counter, true);
				StatusMessage(this, e);
			}
		}
		#endregion


		#region local variables

		private static readonly log4net.ILog _logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

		private bool _didCancel;

		/// <summary>
		/// Flag indicating the user has cancelled processing
		/// </summary>
		public bool Cancel
		{
			set { _didCancel = value; }
		}

		private HtmlHelp _HtmlHelp = new HtmlHelp();

		private string _sFile;
		private bool _bStart;
		private StringFunctions sf = new StringFunctions();
		private string _srcTarget = "_self";


		private string _pinfoBorn = "";
		private string _pinfoDied = "";
		private string _pinfoMarried = "";

		//new tag to text adde in v.9
		private SortedList<string, string> _slTagToText = new SortedList<string, string>();

		private DateTime _newDate = new DateTime();

		private string _pext = ".htm";

		/// <summary>
		/// Collection of individuals read from project
		/// </summary>
		public Individuals MyIndividuals;
		/// <summary>
		/// Collection of children of a family
		/// </summary>
		public Children MyChildren;
		/// <summary>
		/// Collection of families read from the gedcom file
		/// </summary>
		public Families MyFamilies;
		/// <summary>
		/// Collection of sources read from the gedcom file
		/// </summary>
		public Sources MySources;
		/// <summary>
		/// Collection of notes read from the gedcom file
		/// </summary>
		public Notes MyNotes;
		/// <summary>
		/// Collection of surnames read from the gedcom file
		/// </summary>
		public Surnames MySurnames;
		private Gendices MyGendices;
		/// <summary>
		/// File information from the gedcom file
		/// </summary>
		public GedcomFileInfo MyFileInfo;
		private TimeLineEntries MyTimeLineEntries;
		/// <summary>
		/// Collection of locations read from the gedcom file
		/// </summary>
		public Locations MyLocations;
		/// <summary>
		/// Collection of occupations read from the gedcom file
		/// </summary>
		public Occupations MyOccupations;
		//public GedcomLines myGedcomFile;
		private IndiLines MyIndiLines;
		private DescendantOutlines MyDescendantOutlines;
		private Ahnentafels MyAhnentafels;

		private UGedProject m_cfg;
		private LocalApplication localSettings;

		private const short GET_DRIVE = 0;
		private const short GET_DIRECTORY = 1;
		private const short GET_FILENAME = 2;
		private const short GET_EXTENSION = 3;


		private const string Privacy = "[private]";

		private enum RecordTypes
		{
			rtHEAD = 0,
			rtINDI,
			rtFAM,
			rtNOTE,
			rtSOUR,
			rtOBJE,
			rtREPO,
			rtSUBN,
			rtSUBM,
			rtUNKN,
		};

		private bool _RssForSS = false;

		#endregion

		/// <summary>
		/// Class constructor
		/// </summary>
		public GedReader(UGedProject ConfigSettings, LocalApplication LocalSettings, string FileName)
		{
			StartLogging();

			localSettings = LocalSettings;
			m_cfg = ConfigSettings;
			_sFile = FileName;

			if (m_cfg.DoCompileHtml == true)
			{
				_HtmlHelp = new HtmlHelp();
			}

			SetPersonInfoStrings();

			if (m_cfg.UseTagToText == true)
			{
				GetTagText();
			}

			this._MaxChildBirth = m_cfg.MaxChildBirth;
			this._MaxAgeDifference = m_cfg.MaxAgeDifference;
			this._MaxLivingAge = m_cfg.MaxLivingAge;
			this._MinChildBirth = m_cfg.MinChildBirth;
			this._MinMarrAge = m_cfg.MinMarrAge;
			this._AboutEquals = m_cfg.AboutEquals;
			this._MaxIterations = m_cfg.MaxIterations;

			if (m_cfg.PageFileExtension == 0) _pext = ".html";
			if (m_cfg.PageFileExtension == 1) _pext = ".htm";

		}

		private void StartLogging()
		{
			//string sPath = FolderMethods.GetAppPath();
			//string log4netFile = sPath + "\\dlllog.cfg";
			//System.IO.FileInfo configFile = new System.IO.FileInfo(log4netFile);
			//log4net.Config.XmlConfigurator.ConfigureAndWatch(configFile);
			_logger.InfoFormat("Using Uncle GED Class Library ver. {0} ", Assembly.GetExecutingAssembly().GetName().Version.ToString());

		}

		/// <summary>
		/// Class constructor
		/// </summary>
		public GedReader(UGedProject ConfigSettings, LocalApplication LocalSettings, string FileName, bool RssForSS)
		{
			StartLogging();

			localSettings = LocalSettings;
			m_cfg = ConfigSettings;
			_sFile = FileName;

			if (m_cfg.DoCompileHtml == true)
			{
				_HtmlHelp = new HtmlHelp();
			}

			SetPersonInfoStrings();

			if (m_cfg.UseTagToText == true)
			{
				GetTagText();
			}

			this._MaxChildBirth = m_cfg.MaxChildBirth;
			this._MaxAgeDifference = m_cfg.MaxAgeDifference;
			this._MaxLivingAge = m_cfg.MaxLivingAge;
			this._MinChildBirth = m_cfg.MinChildBirth;
			this._MinMarrAge = m_cfg.MinMarrAge;
			this._AboutEquals = m_cfg.AboutEquals;
			this._MaxIterations = m_cfg.MaxIterations;

			if (m_cfg.PageFileExtension == 0) _pext = ".html";
			if (m_cfg.PageFileExtension == 1) _pext = ".htm";

			_RssForSS = RssForSS;
		}


		/// <summary>
		/// Class constructor for read-only ops
		/// </summary>
		public GedReader(string FileName)
		{
			StartLogging();
			localSettings = new LocalApplication();
			m_cfg = new UGedProject();
			m_cfg.Privacy = false;
			_sFile = FileName;
		}
		/// <summary>
		/// Class constructor for instance to use public methods
		/// </summary>
		public GedReader()
		{
			StartLogging();
			localSettings = new LocalApplication();
			m_cfg = new UGedProject();
			m_cfg.Privacy = false;
			_sFile = "";
		}

		private void GetTagText()
		{

			//CHANGE: added tag to test
			//TEST: need to test what happens if tag to text is corrupted or missing
			_slTagToText = new SortedList<string, string>();

			string delimStr = "=";
			char[] delimiter = delimStr.ToCharArray();
			string[] split = null;

			//set the path
			string path = FolderMethods.GetUserPath(); //Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
			string fname = path + "\\tagtotext.txt";

			if (!File.Exists(fname))
			{
				FireStatusMessage("Error getting tag text. Setting UseTagToText to false");
				m_cfg.UseTagToText = false;
				return;
			}

			try
			{
				// Create an instance of StreamReader to read from a file.
				// The using statement also closes the StreamReader.
				using (StreamReader sr = new StreamReader(fname, Encoding.UTF8))
				{
					String line;

					while ((line = sr.ReadLine()) != null)
					{
						//split the line on '=' and add to sorted list
						split = line.Split(delimiter, 2);

						try
						{
							_slTagToText.Add(split[0], split[1]);
						}
						catch (Exception e)
						{
							FireStatusMessage("Error getting tag text : " + e.Message);
						}
					}
				}
			}
			catch (Exception e)
			{
				FireStatusMessage("Error getting tag text. Setting UseTagToText to false (" + e.Message + ")");
				m_cfg.UseTagToText = false;
			}

		}

		private void SetPersonInfoStrings()
		{
			_pinfoBorn = m_cfg.PersonPages.BornString;
			if (_pinfoBorn == "") _pinfoBorn = "b.";
			if (_pinfoBorn == "deg") _pinfoBorn = "&deg;";

			_pinfoDied = m_cfg.PersonPages.DiedString;
			if (_pinfoDied == "") _pinfoDied = "d.";

			_pinfoMarried = m_cfg.PersonPages.MarriedString;
			if (_pinfoMarried == "") _pinfoMarried = "m.";
			if (_pinfoMarried == "times") _pinfoMarried = "&times;";

		}

		#region public methods for processing gedcom
		/// <summary>
		/// Main method that reads and processes the GEDCOM file
		/// </summary>
		/// <remarks>If bFullProcess is true then calls FixFamiliesEach()</remarks>
		/// <param name="GedComFileName">Name of the GEDCOM file to process</param>
		/// <param name="bFullProcess">Flag indicating that the file is to fully processed</param>
		/// <returns>True if successful</returns>
		public bool ProcessGedComFile(string GedComFileName, bool bFullProcess)
		{
			string sFile;
			long NumBytes = 0;
			long BytesRead = 0;

			_didCancel = false;

			MyIndividuals = new Individuals();
			MyChildren = new Children();
			MyFamilies = new Families();
			MySources = new Sources();
			MyNotes = new Notes();
			MySurnames = new Surnames();
			MyFileInfo = new GedcomFileInfo();
			MyTimeLineEntries = new TimeLineEntries();
			MyLocations = new Locations();
			MyOccupations = new Occupations();
			MyIndiLines = new IndiLines();
			MyDescendantOutlines = new DescendantOutlines();
			MyAhnentafels = new Ahnentafels();

			sFile = GedComFileName;
			_sFile = GetFilePart(GedComFileName, GET_FILENAME) + GetFilePart(GedComFileName, GET_EXTENSION);


			m_cfg.FileSource = _sFile;

			FireStatusMessage("Reading " + sFile + "...");
			FileStream fs;
			StreamReader sr;
			try
			{
				fs = new FileStream(sFile, FileMode.Open, FileAccess.Read);
				sr = new StreamReader(fs, Encoding.UTF8);
				NumBytes = fs.Length;
				FireStatusMessage("The file size is: " + NumBytes.ToString() + " bytes");
			}
			catch (Exception ex)
			{
				FireStatusMessage("Error in ProcessGedComFile : " + ex.Message);
				return false;
			}

			//read a line from the gedcom file
			string a;

#if !NO_TRY
			try
			{
#endif
				while ((a = sr.ReadLine()) != null)
				{
					//_logger.DebugFormat("GEDCOM line:={0}", a);
					if (_didCancel)
					{
						goto CLEAN_UP;
					}
					if (a.Trim().Length != 0)
					{
						try
						{
							ProcessGedData(a);  //process each line
						}
						catch (Exception ex)
						{
							FireStatusMessage("Error while reading GEDCOM file: " + ex.Message);
						}
					}
					BytesRead = BytesRead + a.Length;

					if (BytesRead % 1000 == 0)
					{
						FireProgressMessage(String.Format("Processed {0} out of {1} bytes...", BytesRead, NumBytes), NumBytes, BytesRead);
					}
				}
#if !NO_TRY
			}
			catch (Exception ex)
			{
				FireStatusMessage("Error while reading GEDCOM file: " + ex.Message);
				return false;
			}
#endif
			sr.Close();

			if (bFullProcess == true)
			{
				FireStatusMessage("Marking missing spouses...");
				FixFamiliesEach(); //if the husband or wife is an empty string change this to a question mark;
			}

			FireStatusMessage("File process complete!");

			if (_didCancel)
			{
				goto CLEAN_UP;
			}
			if (m_cfg.Privacy)
			{
				FireStatusMessage("Flagging private records...");
				MarkAsPrivate();
			}

			if (_didCancel)
			{
				goto CLEAN_UP;
			}

			_bStart = true;
			_didCancel = false;

			return true;

		CLEAN_UP:
			_sFile = "";
			_didCancel = false;
			MyIndividuals = null;
			MyChildren = null;
			MyFamilies = null;
			MySources = null;
			MyNotes = null;
			MySurnames = null;
			_bStart = true;

			return true;
		}
		#endregion

		#region private methods for processing gedcom
		//locals used by ProcessGedData
		private LifeEvent.EventTypes EventType;
		private RecordTypes RecType;
		private string _xrefid;
		private string _prevtag;
		private string _prevsrcid = "";
		private string _tag2;
		private string _tag1;
		private int _currevent;
		private string _filesrc;
		private string _taglevel1;
		private bool _isevent;
		private int _lineNumber;

		/// <summary>
		/// Called by ProcessGedComFile
		/// </summary>
		/// <param name="lineText"></param>
		private void ProcessGedData(string lineText)
		{
			if (lineText.Trim().Length == 0) return;

			int curLevel = 0;
			string tag = "";
			string data = "";
			string tmp;
			//int posDelim; //was used previous to 10.01 in extracting individual name parts

#if !TEST
         int iSpaceOne;
         int iLineNumber;
#endif

			Individual TheIndividual;
			Child TheChild;
			Family TheFamily;
			Source TheSource;
			Note TheNote;
			LifeEvent NewEvent;
			DateTime dtTempDate;

			const string SLASH = "/";

			if (_bStart)
			{
				EventType = LifeEvent.EventTypes.etUnknown;
				RecType = RecordTypes.rtHEAD;
				_xrefid = "";
				_prevtag = "";
				_bStart = false;
			}

			_lineNumber++; // = myGedcomFile.Add(lineText);

#if TEST
			string[] lineParts = lineText.Split(new Char[] { ' ' }, 3);
			int nextPart = 0;

			if (lineParts.Length < 2)
			{
				//_logger.Debug("Invalid GEDCOM line");
				throw new Exception("Invalid GEDCOM line");
			}

			// first part is always the line number
			// and it must be an int otherwise it is a bad line
			try
			{
				curLevel = int.Parse(lineParts[nextPart++]);
			}
			catch (Exception ex)
			{
				//_logger.Debug("Invalid GEDCOM line (" + ex.Message + ")");
				throw new Exception("Invalid GEDCOM line (" + ex.Message + ")");
			}

			if (lineParts[nextPart].StartsWith("@"))
			{
				if (lineParts.Length < 3)
				{
					//_logger.Debug("Invalid GEDCOM line");
					throw new Exception("Invalid GEDCOM line");
				}

				// this is an xref_id, next part is the tag
				_xrefid = lineParts[nextPart++]; //.Replace("@", "");
				tag = lineParts[nextPart++];
			}
			else
			{
				// no xref_id, first part is tag
				tag = lineParts[nextPart++];
				if (lineParts.Length == 3)
					data = lineParts[nextPart];
			}

			tag = tag.Trim();
#else
         curLevel = System.Convert.ToInt32(sf.Left(lineText, 1));
         iSpaceOne = sf.InStr(3, lineText, " ");
         if (iSpaceOne != 0)
         {
            tag = sf.Trim(sf.Mid(lineText, 3, iSpaceOne - 2));
            data = sf.Right(lineText, sf.Len(lineText) - iSpaceOne);
            data = sf.Trim(data);
         }
         else if (sf.Trim(lineText) != "")
         {
            tag = sf.Trim(sf.Right(lineText, sf.Len(lineText) - 2));
            data = "";
         }
         else
         {
            tag = "";
            data = "";
         }
#endif

			if (curLevel == 2)
			{
				_tag2 = tag;
			}
			else if (curLevel < 2)
			{
				_tag2 = "";
			}

			if (curLevel == 1)
			{
				_tag1 = tag;
			}
			else if (curLevel < 1)
			{
				_tag1 = "";
			}

			//_logger.DebugFormat("GEDCOM Line curLevel:={0} tag:={1}", curLevel, tag);

			if (curLevel == 0)
			{
#if !TEST
            _xrefid = tag;
            tag = data;
            data = "";
#endif
				_isevent = false;
				switch (tag)
				{
					case "HEAD":
						RecType = RecordTypes.rtHEAD;
						break;
					case "INDI":
						TheIndividual = new Individual();
						TheIndividual.ID = _xrefid;
						TheIndividual.LineNumber = _lineNumber;
						MyIndiLines.Add(_xrefid, _lineNumber);
						MyIndividuals.Add(TheIndividual);
						RecType = RecordTypes.rtINDI;
						_currevent = 0;
						break;
					case "FAM":
						TheFamily = new Family();
						TheFamily.ID = _xrefid;
						MyFamilies.Add(TheFamily);
						RecType = RecordTypes.rtFAM;
						_currevent = 0;
						break;
					case "NOTE":
						TheNote = new Note();
						TheNote.ID = _xrefid;
						MyNotes.Add(TheNote);
						RecType = RecordTypes.rtNOTE;
						_currevent = 0;
						break;
					case "SOUR":
						TheSource = new Source();
						TheSource.ID = _xrefid;
						MySources.Add(TheSource);
						RecType = RecordTypes.rtSOUR;
						_currevent = 0;
						break;
					case "OBJE":
						//not used
						RecType = RecordTypes.rtOBJE;
						_currevent = 0;
						break;
					case "REPO":
						//not used
						RecType = RecordTypes.rtREPO;
						_currevent = 0;
						break;
					case "SUBN": //???????
						//not used
						RecType = RecordTypes.rtSUBN;
						_currevent = 0;
						break;
					case "SUBM":
						RecType = RecordTypes.rtSUBM;
						_currevent = 0;
						break;
					default:
						RecType = RecordTypes.rtUNKN;
						_currevent = 0;
						break;
				}
			}
			else
			{
				if (curLevel == 1)
				{
					_taglevel1 = tag;
					_isevent = false;
				}

				switch (tag)
				{
					case "_DETS":
						EventType = LifeEvent.EventTypes.etDeathOfSpouse;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						MyFamilies[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "_MILT":
						EventType = LifeEvent.EventTypes.etMilitary;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "_UNKN":
						EventType = LifeEvent.EventTypes.etUnknown;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = "";
						NewEvent.EventDescription = data;
						switch (RecType)
						{
							case RecordTypes.rtINDI:
								MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
								_isevent = true;
								break;
							case RecordTypes.rtFAM:
								MyFamilies[_xrefid].LifeEvents.Add(NewEvent);
								_isevent = true;
								break;
						}
						break;
					case "_UID":
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].PafUniqueID = data;
						break;
					case "ADDR":
						if (curLevel == 1)
						{
							if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].Address = data;
						}
						break;
					case "ADOP":
						//adoption in child record
						//need special handling for this
						break;
					case "ALIA":
						MyIndividuals[_xrefid].AliasName = data.Replace("/", "");
						break;
					case "_AKA": //PAF uses this. why not use ALIA?;
						MyIndividuals[_xrefid].AliasName = data.Replace("/", "");
						break;
					case "AUTH":
						if (RecType == RecordTypes.rtSOUR)
						{
							MySources[_xrefid].Author = data;
						}
						break;
					case "BIRT":
						EventType = LifeEvent.EventTypes.etBirth;

						//added in 0.7 (not in vb version)
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "BURI":
						//EventType = etBurial
						EventType = LifeEvent.EventTypes.etBurial;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CALN":
						//the source call record
						//I don't use this
						break;
					case "CHIL": //inside a family record;
						TheChild = new Child();
						TheChild.ChildID = data;
						TheChild.FamilyID = _xrefid;
						MyChildren.Add(TheChild);
						if (MyFamilies[_xrefid].HasChildren == false)
						{
							MyFamilies[_xrefid].HasChildren = true;
						}
						break;
					case "CHAN":
						EventType = LifeEvent.EventTypes.etChange;
						_isevent = true;
						break;
					case "CONC": //concatenation of a note;
						if (RecType == RecordTypes.rtNOTE)
						{
							try
							{
								//_logger.DebugFormat("Concatenation of a note with id:={0} data:={1}", _xrefid, data);
								tmp = MyNotes[_xrefid].NoteText;
								if (m_cfg.AddSpaceToConc == true) tmp += " ";
								MyNotes[_xrefid].NoteText = tmp + data;
							}
							catch
							{
								//this is necessary because FTW incorrectly places source notes at level 0
								_logger.Debug("this is necessary because FTW incorrectly places source notes at level 0");
							}
						}
						if (RecType == RecordTypes.rtINDI)
						{ //added for handling gedcoms from PAF
							if ((_tag2 == "SOUR" || _tag1 == "SOUR") && _prevtag != "TEXT")
							{
								tmp = MyIndividuals[_xrefid].Sources[_prevsrcid].Note;
								if (m_cfg.AddSpaceToConc == true) tmp += " ";
								MyIndividuals[_xrefid].Sources[_prevsrcid].Note = tmp + data;
							}
							else if (_prevtag == "TEXT")
							{
								tmp = MyIndividuals[_xrefid].Sources[_prevsrcid].SourceText;
								if (m_cfg.AddSpaceToConc == true) tmp += " ";
								MyIndividuals[_xrefid].Sources[_prevsrcid].SourceText = tmp + data;
							}
							else if (_isevent == true)
							{
								tmp = MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventNote;
								if (m_cfg.AddSpaceToConc == true) tmp += " ";
								MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventNote = tmp + data;
							}
							else
							{
								if (_filesrc != "TMG" && _filesrc != "LEG") //added in 8.0
								{
									//_logger.Debug("Test1");
									tmp = MyIndividuals[_xrefid].Notes;
									if (m_cfg.AddSpaceToConc == true) tmp += " ";
									MyIndividuals[_xrefid].Notes = tmp + data;
								}
								else
								{
									//_logger.Debug("Test2");

									TheNote = new Note();
									TheNote.ID = _xrefid;
									//this should fail if the the note has already been added to the collection
									MyNotes.Add(TheNote);

									tmp = MyNotes[_xrefid].NoteText;
									if (m_cfg.AddSpaceToConc == true) tmp += " ";
									MyNotes[_xrefid].NoteText = tmp + data;
								}
							}
						}
						if (RecType == RecordTypes.rtSOUR)
						{
							switch (_prevtag)
							{
								case "TITL":
									tmp = MySources[_xrefid].Title;
									if (m_cfg.AddSpaceToConc == true) tmp += " ";
									MySources[_xrefid].Title = tmp + data;
									break;
								case "PUBL":
									tmp = MySources[_xrefid].PublicationInfo;
									if (m_cfg.AddSpaceToConc == true) tmp += " ";
									MySources[_xrefid].PublicationInfo = tmp + data;
									break;
							}
						}
						break;
					case "CONT":
						//continuation of a previous piece of data
						//could be an address or even a note
						switch (_prevtag)
						{
							case "ADDR":
								if (RecType == RecordTypes.rtINDI)
								{
									tmp = MyIndividuals[_xrefid].Address;
									MyIndividuals[_xrefid].Address = tmp + "<br/>" + data;
								}
								break;
							case "NOTE":
								if (RecType == RecordTypes.rtNOTE)
								{
									tmp = MyNotes[_xrefid].NoteText;
									MyNotes[_xrefid].NoteText = tmp + "<br/>" + data;
								}
								if (RecType == RecordTypes.rtINDI)
								{
									if (_tag2 == "SOUR" || _tag1 == "SOUR")
									{
										tmp = MyIndividuals[_xrefid].Sources[_prevsrcid].Note;
										MyIndividuals[_xrefid].Sources[_prevsrcid].Note = tmp + data;
									}
									else if (_isevent == true)
									{
										tmp = MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventNote;
										MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventNote = tmp + " " + data;
									}
									else
									{
										TheNote = new Note();
										TheNote.ID = _xrefid;
										//this should fail if the the note has already been added to the collection
										MyNotes.Add(TheNote);

										tmp = MyNotes[_xrefid].NoteText;
										MyNotes[_xrefid].NoteText = tmp + "<br/>" + data;
									}
								}
								if (RecType == RecordTypes.rtSOUR)
								{ //added in 3.01
									tmp = MySources[_xrefid].Note;
									MySources[_xrefid].Note = tmp + "<br/>" + data;
								}
								break;
							case "AUTH": //author of source;
								if (RecType == RecordTypes.rtSOUR)
								{
									tmp = MySources[_xrefid].Author;
									MySources[_xrefid].Author = tmp + "<br/>" + data;
								}
								break;
							case "TITL"://title of source;
								if (RecType == RecordTypes.rtSOUR)
								{
									tmp = MySources[_xrefid].Title;
									MySources[_xrefid].Title = tmp + "<br/>" + data;
								}
								break;
							case "TEXT"://text from source;
								if (RecType == RecordTypes.rtSOUR)
								{
									tmp = MySources[_xrefid].SourceText;
									MySources[_xrefid].SourceText = tmp + "<br/>" + data;
								}
								else if (RecType == RecordTypes.rtINDI)
								{
									tmp = MyIndividuals[_xrefid].Sources[_prevsrcid].SourceText;
									MyIndividuals[_xrefid].Sources[_prevsrcid].SourceText = tmp + data;
								}
								break;
							case "PAGE"://used by tmg for citations;
								if (RecType == RecordTypes.rtINDI)
								{
									tmp = MyIndividuals[_xrefid].Sources[_prevsrcid].Page;
									MyIndividuals[_xrefid].Sources[_prevsrcid].Page = tmp + " " + data;
								}
								break;
							default:
								break;
							//there are others but we will deal with them later
						}
						break;
					case "DATA":
						//when a source appears in a individual record
						//and text is associeted with the source note
						//need special handling for this
						break;
					case "DATE":
						if (RecType != RecordTypes.rtHEAD)
						{
							data = data.Trim();

							//normalize the date
							if (sf.IsDate(data))
							{
								string[] splitDate = data.Split(new Char[] { ' ', ',', '.', ':' }, 3);

								//FIX: partial dates such as "JUL 1877" were being displayed as "01 July 1877"
								switch (splitDate.Length)
								{
									case 1:
										data = sf.DateValue(data).ToString("yyyy");
										break;
									case 2:
										//is it a "month year" or "day month"?
										Int32 int32Val;
										if (Int32.TryParse(splitDate[0], out int32Val) == true)
											data = sf.DateValue(data).ToString("dd MMMM");
										else
											data = sf.DateValue(data).ToString("MMMM yyyy");
										break;
									case 3:
										data = sf.DateValue(data).ToString("dd MMMM yyyy");
										break;
									default:
										data = sf.DateValue(data).ToString("dd MMMM yyyy");
										break;
								}
							}
							else
							{
								//added in 2.5
								data = FixDate(data);
							}

							//make sure we didn't get here by mistake
							if (_currevent == 0 && EventType != LifeEvent.EventTypes.etChange) break;

							//process the event type
							switch (EventType)
							{
								case LifeEvent.EventTypes.etUnknown:
									switch (RecType)
									{
										case RecordTypes.rtINDI:
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
											break;
										case RecordTypes.rtFAM:
											MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
											break;
									}
									break;
								case LifeEvent.EventTypes.etDeathOfSpouse:
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBirth:
									if (RecType == RecordTypes.rtINDI && MyIndividuals[_xrefid].BirthDateText.Length == 0) MyIndividuals[_xrefid].BirthDateText = data;
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etDeath:
									if (RecType == RecordTypes.rtINDI && MyIndividuals[_xrefid].DeathDateText.Length == 0) MyIndividuals[_xrefid].DeathDateText = data;
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBurial:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etOccupation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etMarriage:
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].MarriageDateText = data;
									break;
								case LifeEvent.EventTypes.etResidence:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etDivorce:
									if (RecType == RecordTypes.rtFAM)
									{
										MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
										MyFamilies[_xrefid].DivorceDateText = data;
									}
									break;
								case LifeEvent.EventTypes.etImmigration:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etMilitary:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etOther:
									//if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									//break;

									switch (RecType)
									{
										case RecordTypes.rtINDI:
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
											break;
										case RecordTypes.rtFAM:
											MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
											break;
									}
									break;

								case LifeEvent.EventTypes.etElected:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etExcommunicated:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etMission:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBaptismLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBaptism:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBar_Mitzvah:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBat_Mitzvah:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etBlessing:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etCaste:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etCensus:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etChristening:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etConfirmation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etConfirmationLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etCremation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etEducation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etEmigration:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etEndowmentLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etFirstCommunion:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etGraduation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etNaturalization:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etOrdination:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etProbate:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etProperty:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etReligion:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etRetirement:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etSealedToParentsLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etSSN:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etWill:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDateString = data;
									break;
								case LifeEvent.EventTypes.etChange:
									if (RecType == RecordTypes.rtINDI)
									{
										if (sf.IsDate(data))
										{
											dtTempDate = sf.DateValue(data);
											if (dtTempDate > MyIndividuals[_xrefid].LastChangeDate)
											{
												MyIndividuals[_xrefid].LastChangeDate = dtTempDate;
											}
										}
									}
									if (RecType == RecordTypes.rtFAM)
									{
										if (sf.IsDate(data))
										{
											dtTempDate = sf.DateValue(data);
											if (dtTempDate > MyFamilies[_xrefid].LastChangeDate)
											{
												MyFamilies[_xrefid].LastChangeDate = dtTempDate;
											}
										}
									}
									break;
							}
						}
						else
						{
							MyFileInfo.CreationDate = data;
						}
						break;
					case "DEAT":
						EventType = LifeEvent.EventTypes.etDeath;

						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "DIV": //occurs inside a family record;
						EventType = LifeEvent.EventTypes.etDivorce;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents.Add(NewEvent);
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "EMAIL":
						if (RecType == RecordTypes.rtSUBM)
						{
							MyFileInfo.SubmitterEmail = data;
						}
						break;
					case "EVEN":
						//this is a special event defined by type
						//there could be many of these
						EventType = LifeEvent.EventTypes.etOther;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents.Add(NewEvent);
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "FAMC":
						MyIndividuals[_xrefid].FamilyID = data;//added in ver 1.2;
						TheChild = new Child();
						TheChild.ChildID = _xrefid;
						TheChild.FamilyID = data;
						MyChildren.Add(TheChild);
						break;
					case "FAMS":
						//individual is a spouse in a particular family
						//attempt to create a new family
						TheFamily = new Family();
						TheFamily.ID = data;
						MyFamilies.Add(TheFamily);
						MyIndividuals[_xrefid].HasOwnFamily = true;
						MyIndividuals[_xrefid].FamList.Add(data, data);
						switch (MyIndividuals[_xrefid].Sex)
						{
							case "M":
								MyFamilies[data].Husband = _xrefid;
								break;
							case "F":
								MyFamilies[data].Wife = _xrefid;
								break;
						}
						break;
					case "FILE":
						if (RecType == RecordTypes.rtHEAD)
						{
							MyFileInfo.SourceFile = data;
						}
						break;
					case "HUSB":
						if (data != "@I0@") MyFamilies[_xrefid].Husband = data;
						break;
					case "IMMI":
						EventType = LifeEvent.EventTypes.etImmigration;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "MARR":
						//CHANGE: added a DidMarry flag in the family object
						if (RecType == RecordTypes.rtFAM)
							MyFamilies[_xrefid].DidMarry = true;

						EventType = LifeEvent.EventTypes.etMarriage;
						_currevent++;
						break;
					case "MEDI":
						//media of the source
						if (RecType == RecordTypes.rtSOUR)
						{
							MySources[_xrefid].Media = data;
						}
						break;
					case "NAME":
						if (RecType == RecordTypes.rtHEAD)
						{
							MyFileInfo.SourceName = data;
						}
						if (RecType == RecordTypes.rtSUBM)
						{
							MyFileInfo.SubmitterName = data;
						}
						if (curLevel == 1)
						{
							if (RecType == RecordTypes.rtINDI && MyIndividuals[_xrefid].GotName == false)
							{
								MyIndividuals[_xrefid].Surname = data; //in case it is just a single name (ex. <Living>)

								if (data.Contains(SLASH))
								{
									string foreNames = "";
									string surName = "";
									string suffix = "";
									ExtractName(data, ref foreNames, ref surName, ref suffix);

									MyIndividuals[_xrefid].Forenames = foreNames;
									MyIndividuals[_xrefid].Surname = surName;
									MyIndividuals[_xrefid].Suffix = suffix;
								}

								if (m_cfg.SurnamesAllCaps && MyIndividuals[_xrefid].Surname != "")
								{
									MyIndividuals[_xrefid].Surname = sf.UCase(MyIndividuals[_xrefid].Surname);
								}

								MyIndividuals[_xrefid].GotName = true;
							}
						}
						break;
					case "NATI":
						EventType = LifeEvent.EventTypes.etNationality;
						break;
					case "NOTE":
						//_logger.DebugFormat("Processing NOTE tag. RecType:={0} _fileSrc:={1} _tag1:={2} _tag2:={3} data:={4} _xrefid:={5}",
						//RecType.ToString(), _filesrc, _tag1, _tag2, data, _xrefid);

						if (RecType == RecordTypes.rtINDI)
						{
							if (_tag2 == "SOUR" || _tag1 == "SOUR")
							{
								MyIndividuals[_xrefid].Sources[_prevsrcid].Note = data;
							}
							else
							{
								switch (_filesrc)
								{
									case "PAF":
										TheNote = new Note(); //trying to fix problem btwn the way that PAF and FTW do notes;
										TheNote.ID = _xrefid;
										MyNotes.Add(TheNote); //this should fail if the the note has already been added to the collection;
										MyNotes[_xrefid].NoteText = data;
										MyIndividuals[_xrefid].Notes = _xrefid;
										break;
									case "FTM":
										TheNote = new Note(); //trying to fix problem btwn the way that FTM does notes;
										TheNote.ID = data;
										MyNotes.Add(TheNote); //this should fail if the the note has already been added to the collection;
										MyIndividuals[_xrefid].Notes = data;
										break;
									case "FTW":
										MyIndividuals[_xrefid].Notes = data;
										break;
									case "LEG":
									case "TMG":
										if ((_tag2 == "NOTE" || _tag1 == "NOTE") && _isevent == true)
										{
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventNote = data;
										}
										else
										{
											MyIndividuals[_xrefid].Notes = data;

											//TheNote = new Note();
											//TheNote.ID = _xrefid;
											//MyNotes.Add(TheNote); //this should fail if the the note has already been added to the collection;
											//MyNotes[_xrefid].NoteText = data;
											//MyIndividuals[_xrefid].Notes = _xrefid;
										}
										break;
								}
							}
						}
						else if (RecType == RecordTypes.rtFAM)
						{ //added ver. 2.07 to handle marriage notes
							switch (_filesrc)
							{
								case "PAF":
									TheNote = new Note(); //trying to fix problem btwn the way that PAF and FTW do notes;
									TheNote.ID = _xrefid;
									MyNotes.Add(TheNote);//this should fail if the the note has already been added to the collection;
									MyNotes[_xrefid].NoteText = data;
									MyFamilies[_xrefid].Notes = _xrefid;
									break;
								case "FTM":
									MyFamilies[_xrefid].Notes = data;
									break;
								case "FTW":
									MyFamilies[_xrefid].Notes = data;
									break;
								case "LEG":
									MyFamilies[_xrefid].Notes = data;
									break;
								default:
									TheNote = new Note(); 
									TheNote.ID = _xrefid;
									MyNotes.Add(TheNote);
									MyNotes[_xrefid].NoteText = data;
									MyFamilies[_xrefid].Notes = _xrefid;
									break;
							}
						}
						else if (RecType == RecordTypes.rtSOUR)
						{ //added in 3.01
							MySources[_xrefid].Note = data;
						}
						else if (RecType == RecordTypes.rtNOTE)
						{
							MyNotes[_xrefid].NoteText = data;
						}
						break;
					case "OCCU":
						EventType = LifeEvent.EventTypes.etOccupation;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						//CHANGE: Occupations are now handled differently
						//NewEvent.EventText = "Occupation: " + data;
						NewEvent.EventDescription = data;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "PAGE":
						//used in source not used here
						//started using in 3.01
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].Sources[_prevsrcid].Page = data;
						break;
					case "ABBR":
						//used in source not used here
						//started using in 5.01
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].Sources[_prevsrcid].Abbreviation = data;
						break;
					case "PUBL":
						if (RecType == RecordTypes.rtSOUR)
						{
							MySources[_xrefid].PublicationInfo = data;
						}
						break;
					case "PLAC":
						//CHANGE: some locations have punctuation in front of them when displayed in locations page "., Belgium, Flanders - World War I" from test file FTW.GED (should strip trailing punctuation from location as this is coming from GEDCOM)   
						if (m_cfg.LocStripTrailingPunc == true)
						{
							data = StripLocTrailingPunc(data);
						}

						if (RecType != RecordTypes.rtHEAD)
						{
							//make sure we didn't get here by mistake
							if (_currevent == 0) break;

							switch (EventType)
							{

								case LifeEvent.EventTypes.etUnknown:
									switch (RecType)
									{
										case RecordTypes.rtINDI:
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
											break;
										case RecordTypes.rtFAM:
											MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
											break;
									}
									break;
								case LifeEvent.EventTypes.etOther:
									switch (RecType)
									{
										case RecordTypes.rtINDI:
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
											break;
										case RecordTypes.rtFAM:
											MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
											break;
									}
									break;
								case LifeEvent.EventTypes.etDeathOfSpouse:
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBirth:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].BirthPlace = data;
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etDeath:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].DeathPlace = data;
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBurial:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etOccupation:
									if (RecType == RecordTypes.rtINDI)
									{
										if (MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDescription == "")
										{
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventDescription = data;
										}
										else
										{
											MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
										}
									}
									break;
								case LifeEvent.EventTypes.etMarriage:
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].MarriagePlace = data;
									break;
								case LifeEvent.EventTypes.etResidence:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etDivorce:
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etNationality:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].Nationality = data;
									break;
								case LifeEvent.EventTypes.etImmigration:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etMilitary:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								//case LifeEvent.EventTypes.etOther:
								//   if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
								//   break;
								case LifeEvent.EventTypes.etElected:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etExcommunicated:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etMission:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBaptismLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBaptism:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBar_Mitzvah:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBat_Mitzvah:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etBlessing:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etCaste:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etCensus:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etChristening:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etConfirmation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etConfirmationLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etCremation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etEducation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etEmigration:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etEndowmentLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etFirstCommunion:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etGraduation:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etNaturalization:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etOrdination:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etProbate:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etProperty:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etReligion:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etRetirement:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etSealedToParentsLDS:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etSSN:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
								case LifeEvent.EventTypes.etWill:
									if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventPlace = data;
									break;
							}
						}
						break;
					case "REFN":
						if (RecType == RecordTypes.rtINDI)
						{
							MyIndividuals[_xrefid].ReferenceNumber = data;
						}
						if (RecType == RecordTypes.rtFAM)
						{
							MyFamilies[_xrefid].ReferenceNumber = data;
						}
						break;
					case "REPO":
						//used in sources
						break;
					case "RESI":
						EventType = LifeEvent.EventTypes.etResidence;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "SEX":
						MyIndividuals[_xrefid].Sex = data;
						break;
					case "SOUR":
						if (RecType == RecordTypes.rtHEAD)
						{ //this is the source of the GEDCOM file
							_filesrc = sf.UCase(sf.Left(data, 3));
							MyFileInfo.Source = data;

						}
						if (sf.Left(data, 1) == "@")
						{
							TheSource = new Source();
							TheSource.ID = data;
							_prevsrcid = data;
							TheSource.EventTag = _taglevel1;
							switch (RecType)
							{
								case RecordTypes.rtINDI: MyIndividuals[_xrefid].Sources.Add(TheSource);
									break;
								case RecordTypes.rtFAM: MyFamilies[_xrefid].Sources.Add(TheSource);
									break;
							}
						}
						break;
					case "TEXT":
						if (_prevsrcid != "")
						{
							if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].Sources[_prevsrcid].SourceText = data;
						}
						break;
					case "TIME":
						if (RecType == RecordTypes.rtHEAD)
						{
							MyFileInfo.CreationTime = data;
						}
						break;
					case "TITL":
						if (RecType == RecordTypes.rtSOUR)
						{
							MySources[_xrefid].Title = data;
						}
						if (RecType == RecordTypes.rtINDI && curLevel == 1)
						{
							MyIndividuals[_xrefid].Title = data;
						}
						break;
					case "TYPE":
						if (_prevtag == "EVEN") //&& RecType == RecordTypes.rtINDI)
						{
							if (data == "Occupation")
							{
								MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventType = LifeEvent.EventTypes.etOccupation;
								tmp = MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText;
								MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText = "Occupation: " + tmp;
							}
							else
							{
								if (RecType == RecordTypes.rtFAM)
								{
									tmp = MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText;
									if (tmp != "Event")
									{
										MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText = data + " " + tmp;
									}
									else
									{
										MyFamilies[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText = data;
									}
								}

								if (RecType == RecordTypes.rtINDI)
								{
									tmp = MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText;
									if (tmp != "Event")
									{
										MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText = data + " " + tmp;

									}
									else
									{
										MyIndividuals[_xrefid].LifeEvents[_xrefid + _currevent.ToString()].EventText = data;
									}
								}
							}
						}
						break;
					case "VERS":
						//version of software that produced this
						if (RecType == RecordTypes.rtHEAD && MyFileInfo.Version == "")
						{
							MyFileInfo.Version = data;
						}
						break;
					case "WIFE":
						if (data != "@I0@") MyFamilies[_xrefid].Wife = data;
						break;
					case "_ELEC":
						EventType = LifeEvent.EventTypes.etElected;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "_EXCM":
						EventType = LifeEvent.EventTypes.etExcommunicated;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "_MISN":
						EventType = LifeEvent.EventTypes.etMission;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "BAPL":
						EventType = LifeEvent.EventTypes.etBaptismLDS;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "BAPM":
						EventType = LifeEvent.EventTypes.etBaptism;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "BARM":
						EventType = LifeEvent.EventTypes.etBar_Mitzvah;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "BASM":
						EventType = LifeEvent.EventTypes.etBat_Mitzvah;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "BLES":
						EventType = LifeEvent.EventTypes.etBlessing;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CAST":
						EventType = LifeEvent.EventTypes.etCaste;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CENS":
						EventType = LifeEvent.EventTypes.etCensus;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CHR":
						EventType = LifeEvent.EventTypes.etChristening;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CONF":
						EventType = LifeEvent.EventTypes.etConfirmation;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CONL":
						EventType = LifeEvent.EventTypes.etConfirmationLDS;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "CREM":
						EventType = LifeEvent.EventTypes.etCremation;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "EDUC":
						EventType = LifeEvent.EventTypes.etEducation;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "EMIG":
						EventType = LifeEvent.EventTypes.etEmigration;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "ENDL":
						EventType = LifeEvent.EventTypes.etEndowmentLDS;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "FCOM":
						EventType = LifeEvent.EventTypes.etFirstCommunion;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "GRAD":
						EventType = LifeEvent.EventTypes.etGraduation;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "NATU":
						EventType = LifeEvent.EventTypes.etNaturalization;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "ORDN":
						EventType = LifeEvent.EventTypes.etOrdination;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "PROB":
						EventType = LifeEvent.EventTypes.etProbate;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "PROP":
						EventType = LifeEvent.EventTypes.etProperty;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "RELI":
						EventType = LifeEvent.EventTypes.etReligion;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "RETI":
						EventType = LifeEvent.EventTypes.etRetirement;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "SLGC":
						EventType = LifeEvent.EventTypes.etSealedToParentsLDS;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "SSN":
						EventType = LifeEvent.EventTypes.etSSN;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					case "WILL":
						EventType = LifeEvent.EventTypes.etWill;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
					default:
						//added in 10.02 to handle freeform custom fact types
						EventType = LifeEvent.EventTypes.etOther;
						_currevent++;
						NewEvent = new LifeEvent();
						NewEvent.ID = _xrefid + _currevent.ToString();
						NewEvent.EventType = EventType;
						NewEvent.EventText = GetEventText(EventType, data, tag);
						NewEvent.EventDescription = data;
						if (RecType == RecordTypes.rtFAM) MyFamilies[_xrefid].LifeEvents.Add(NewEvent);
						if (RecType == RecordTypes.rtINDI) MyIndividuals[_xrefid].LifeEvents.Add(NewEvent);
						_isevent = true;
						break;
				} //end of level 1 tags
			}

			//don't count continuation and concatenation
			//as previous tag
			if (tag != "CONT" && tag != "CONC")
			{
				_prevtag = tag;
			}

		}

		private void ExtractName(string data, ref string foreNames, ref string surName, ref string suffix)
		{
			if (data.Length == 0)
			{
				foreNames = "";
				surName = "";
				suffix = "";
				return;
			}

			//find the position of /
			char[] delims = new char[] { '/' };
			string[] nameParts = new string[] { };

			nameParts = data.Split(delims);

			if (nameParts.Length == 1)
			{
				//single name
				foreNames = "";
				surName = nameParts[0].Trim();
				suffix = "";
			}
			if (nameParts.Length == 2)
			{
				//single name
				foreNames = nameParts[0].Trim();
				surName = nameParts[1].Trim();
				suffix = "";
			}
			if (nameParts.Length > 2)
			{
				//single name
				foreNames = nameParts[0].Trim();
				surName = nameParts[1].Trim();
				suffix = nameParts[2].Replace(",", "").Trim();
			}

		}


		/// <summary>
		/// strip trailing puncuation from PLAC data
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public string StripLocTrailingPunc(string data)
		{
			string tmp = data;
			char[] trimChars = new char[] { ':', ';', ',', '.', ' ' };

			if (data.Length == 0) return data;

			tmp = data.TrimEnd(trimChars);

			return tmp;
		}

		/// <summary>
		/// Trims leading punctuation from PLAC data
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public string StripLocLeadingPunc(string data)
		{
			string tmp = data;
			char[] trimChars = new char[] { ':', ';', ',', '.', ' ' };

			if (data.Length == 0) return data;

			tmp = data.TrimStart(trimChars);

			return tmp;
		}

		#endregion

		#region Save as HTM
		/// <summary>
		/// Main Method that builds the web pages.
		/// </summary>
		/// <remarks>
		/// <see cref="ProcessGedComFile"/> must be called first before this method is called.
		/// </remarks>
		public void SaveAsHTM()
		{

#if !NO_TRY
			try
			{
#endif
				if (m_cfg.CreateGendexFile == true)
				{
					MyGendices = new Gendices();
				}

				if (m_cfg.SourcesInNewWindow == false)
					_srcTarget = "_self";
				else
					_srcTarget = "_blank";

				FireStatusMessage("Writing HTML Pages...");

				_didCancel = false;

				//warn that files may be overwritten
				string[] TestFiles = Directory.GetFiles(m_cfg.HtmlPath, "*.htm*");
				if (TestFiles.Length != 0)
				{
					FireStatusMessage("The directory " + m_cfg.HtmlPath + " contains HTML files. Some files may be overwritten.");
				}

				//if compiling HTML then need to create supporting files
				if (m_cfg.DoCompileHtml == true)
				{
					_HtmlHelp = new HtmlHelp();
					string htmlHelpCompiler = m_cfg.HtmlHelpCompiler + "\\hhc.exe";
					_HtmlHelp.SetNewProject(m_cfg.HtmlPath, m_cfg.ProjectName, m_cfg.HtmlPath + "\\index" + _pext, htmlHelpCompiler);
					_HtmlHelp.OpenProjectFile();
					_HtmlHelp.OpenContentsFile();
				}

				//if compiling HTML add to the TOC
				if (m_cfg.DoCompileHtml == true)
				{
					_HtmlHelp.AddFileToProject(m_cfg.HtmlPath + "\\index" + _pext);
					_HtmlHelp.AddFileToContents("Table Of Contents", m_cfg.HtmlPath + "\\index" + _pext);
				}

				//copy the CSS file if config says so
				if (m_cfg.CSSOption == 2 && m_cfg.CSSFile.Length != 0)
				{
					CopyCssFile();
				}

				//build either person pages or family pages
				if (m_cfg.PageStyle == 1)
				{
					FireStatusMessage("Building Person Pages..");
					if (BuildPersonPages() == false)
					{
						goto HTML_CLEAN_UP;
					}
					if (_didCancel)
					{
						goto HTML_CLEAN_UP;
					}

				}
				else
				{
					FireStatusMessage("Building Family Pages..");
					if (BuildFamilyPages() == false)
					{
						goto HTML_CLEAN_UP;
					}
					if (_didCancel)
					{
						goto HTML_CLEAN_UP;
					}
				}

				FireStatusMessage("Building Index Page...");
				BuildIndexEach();
				if (_didCancel) goto HTML_CLEAN_UP;

				FireStatusMessage("Building Sources Page...");
				BuildBiblio();
				if (_didCancel) goto HTML_CLEAN_UP;

				FireStatusMessage("Building Surname Page...");
				BuildSurnamePage();
				if (_didCancel) goto HTML_CLEAN_UP;

				if (m_cfg.CreateStatsPage == true)
				{
					FireStatusMessage("Building Stats Page...");
					BuildStatisticsPage();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				if (m_cfg.CreateGendexFile == true)
				{
					FireStatusMessage("Building Gendex file...");
					BuildGendexFile();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				//added in 3.01
				if (m_cfg.CreateChronoPage == true)
				{
					FireStatusMessage("Building Chronology Page...");
					BuildTimeLinePage();
					if (_didCancel) goto HTML_CLEAN_UP;
				}


				//added in 3.02
				if (m_cfg.CreateLocPage == true)
				{
					FireStatusMessage("Building Locations Page...");
					BuildLocationsReport();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				//added in 3.03
				if (m_cfg.CreateOccupationsPage == true)
				{
					FireStatusMessage("Building Occupations Page...");
					BuildOccupationsReport();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				//added in 9.0
				if (m_cfg.CreateRssFeed == true)
				{
					FireStatusMessage("Building RSS feed...");
					BuildRssFeed();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				//added in 10.1
				if (m_cfg.DotlCreateDescendantOutline == true)
				{
					FireStatusMessage("Building Descendant Outlines...");
					BuildDescendantOutlines();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				//added in 10.2
				if (m_cfg.AhnCreateAhnentafels == true)
				{
					FireStatusMessage("Building Ahnentafels...");
					BuildAhnentafels();
					if (_didCancel) goto HTML_CLEAN_UP;
				}

				//move build TOC to end of list in 10.01
				BuildTableOfContents();
				if (_didCancel) goto HTML_CLEAN_UP;


				_didCancel = false;
				FireStatusMessage("HTML files have been created in " + m_cfg.HtmlPath + ".");

				string errMsg = "";

				if (m_cfg.DoCompileHtml == true)
				{
					FireStatusMessage("Please wait. Compiling help file to " + UncleGED.Utils.Tools.LongDirFix(_HtmlHelp.PathToCompiledHtmlFile(), 30) + "...");
					_HtmlHelp.CloseProjectFile();
					_HtmlHelp.CloseContentsFile();
					_HtmlHelp.CompileProject(ref errMsg);

					if (errMsg.Length > 0)
					{
						FireErrorMessage("Error compiling HTML: " + errMsg);
					}
				}

				return;

			HTML_CLEAN_UP:
				_didCancel = false;

				return;
#if !NO_TRY
			}
			catch (Exception ex)
			{
				FireStatusMessage("The following error occured in SaveAsHTML while processing the GEDCOM file:" + '\n' + "Error: " + ex.ToString());
				//FireStatusMessage("Incomplete");
				_didCancel = false;
			}
#endif
		}


		private Ancestors _ancestors = new Ancestors();
		/// <summary>
		/// Builds one or more Ahnentafels
		/// </summary>
		private void BuildAhnentafels()
		{
			//first check config settings and make sure they are valid
			//if not, either correct or abort.
			if (m_cfg.AhnIndividuals.Length == 0)
			{
				FireStatusMessage("No individuals listed for ahnentafels. Unable to continue.");
				return;
			}
			if (m_cfg.AhnNumGenerations < 1 || m_cfg.AhnNumGenerations > 25)
			{
				FireStatusMessage("Invalid number of generations reverting to 10. Valid values are 1 to 25.");
				m_cfg.AhnNumGenerations = 25;
			}

			//create an array of indviduals to process from
			//the comma-delimited list supplied in the config file
			char[] delims = new char[] { ',' };
			string[] individuals = new string[] { };

			individuals = m_cfg.AhnIndividuals.Split(delims);

			string id = "";

			//loop through individuals and create descendant charts for each one
			for (int i = 0; i < individuals.Length; i++)
			{
				_ancestors = new Ancestors();
				//call into GetAncestors - a recursive function that operates on the individual(s) 
				//selected and their descendants.
				id = "@I" + individuals[i].Trim() + "@";
				StartAhnentafel(id);
			}
		}

		///<summary>Start a ahnentafel</summary>
		/// <param name="StartID">Indvidual who starts this outline</param>
		private void StartAhnentafel(string StartID)
		{
			string file; //the web page file
			string link; //a local link to this file
			FileStream fs1;
			StreamWriter sw1;
			string q = "\""; //quote string

			//the StringBuilder that holds the HTML that gets written to the file
			StringBuilder sbAhnentafel = new StringBuilder();

			string rin = StartID.Replace("@", " ").Trim();
			//create the header for this page
			if (m_cfg.FamFilePrefix == "fam")
			{
				file = m_cfg.HtmlPath + "\\ahn_" + rin + _pext;
				link = "ahn_" + rin + _pext;
			}
			else
			{
				file = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_ahn_" + rin + _pext;
				link = m_cfg.FamFilePrefix + "_ahn_" + rin + _pext;
			}

			fs1 = new FileStream(file, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			string pageTitle = "";
			//get the persons name
			string personName = "";

			if (MyIndividuals[StartID].Title.Length != 0)
				personName = MyIndividuals[StartID].Title + " ";

			if (MyIndividuals[StartID].Forenames.Length != 0)
				personName += MyIndividuals[StartID].Forenames + " ";

			if (MyIndividuals[StartID].Surname.Length != 0)
				personName += MyIndividuals[StartID].Surname;

			if (MyIndividuals[StartID].Suffix.Length != 0)
				personName += ", " + MyIndividuals[StartID].Suffix;

			personName = personName.Trim();

			pageTitle = "Ahnentafel for " + personName;

			MyAhnentafels.Add(pageTitle, link, StartID);

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(file);
				_HtmlHelp.AddFileToContents(pageTitle, file);
			}

			WriteHtmlHeader(sw1, pageTitle);
			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + q + "center" + q + "><img src=" + q + m_cfg.BannerFile + q + "></p>");
			}

			sw1.WriteLine("<h1 align=\"center\">" + pageTitle + "</h1>");
			sw1.WriteLine(GetMenu());
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			//Build the list of ancestors
			GetAncestors(StartID, 1, 1);

			string line = "";
			personName = "";
			string personInfo = "";
			link = "";
			rin = "";

			bool isGenHasAncestors = false;
			//create content here...
			for (int genCount = 1; genCount <= m_cfg.AhnNumGenerations; genCount++)
			{
				line = String.Format("<p style=\"text-align:center;\">Generation {0}</p>\r\n<p>", genCount);
				
				isGenHasAncestors = false;

				foreach (Ancestor a in _ancestors)
				{
					if (a.GenerationNumber == genCount)
					{
						isGenHasAncestors = true;

						//get the persons name
						personName = "";
						if (MyIndividuals[a.PersonId].Title.Length != 0)
							personName = MyIndividuals[a.PersonId].Title + " ";

						if (MyIndividuals[a.PersonId].Forenames.Length != 0)
							personName += MyIndividuals[a.PersonId].Forenames + " ";

						if (MyIndividuals[a.PersonId].Surname.Length != 0)
							personName += MyIndividuals[a.PersonId].Surname;

						if (MyIndividuals[a.PersonId].Suffix.Length != 0)
							personName += ", " + MyIndividuals[a.PersonId].Suffix;

						personName = personName.Trim();

						personInfo = String.Format("({0}) ", GetSexString(MyIndividuals[a.PersonId].Sex));

						if (MyIndividuals[a.PersonId].BirthDateText != "")
						{
							personInfo += String.Format(", {0} {1}", _pinfoBorn, MyIndividuals[a.PersonId].BirthDateText);
						}

						if (MyIndividuals[a.PersonId].DeathDateText != "")
						{
							personInfo += String.Format(", {0} {1}", _pinfoDied, MyIndividuals[a.PersonId].DeathDateText);
						}

						if (m_cfg.AhnSSLinks == true)
						{
							rin = a.PersonId.Replace("@", " ").Trim();
							rin = rin.Replace("I", " ").Trim();
							int idNumber = System.Int32.Parse(rin);

							link = String.Format("<%=Page.PersonHREF({0})%>", idNumber);
						}
						else
						{
							link = MyIndividuals[a.PersonId].LinkPage;
						}

							line += String.Format("{0}.&nbsp;<a href=\"{1}\">{2}</a> - {3}<br/>\r\n",
								a.AncestorNumber, link, personName, personInfo);
					}
				}
				if (isGenHasAncestors)
				{
					line += "\r\n</p>\r\n";
					sbAhnentafel.Append(line);
				}
			}
			sw1.WriteLine(sbAhnentafel.ToString());

			//create the footer for this page
			WriteHtmlFooter(sw1);

			sw1.Close();
			fs1.Close();

		}

		/// <summary>
		/// GetAncestors is a recursive function used to build an ahnentafel
		/// </summary>
		/// <param name="PersonId">The Individuals ID</param>
		/// <param name="Generation">the generation that this individual belongs to</param>
		/// <param name="AncestorNumber">the ancestor number assigned to this indvidual</param>
		private void GetAncestors(string PersonId, int Generation, int AncestorNumber)
		{
			Ancestor ancestor = new Ancestor();
			ancestor.PersonId = PersonId;
			ancestor.AncestorNumber = AncestorNumber; 
			ancestor.GenerationNumber = Generation;
			_ancestors.Add(ancestor);

			int nextGeneration = Generation + 1;
			int fatherAncestorNumber = AncestorNumber * 2;
			int motherAncestorNumber = (AncestorNumber * 2) + 1;
			string fatherID = "";
			string motherID = "";

			string message = String.Format("Person ID=:{0} Generation:={1} AncestorNumber:={2}", PersonId, Generation, AncestorNumber);
			FireDebugMessage(message);

			//find the parents of this person
			string famKey = MyIndividuals[PersonId].FamilyID + MyIndividuals[PersonId].ID;

			{
				Child child;
				try
				{
					child = MyChildren[famKey];
				}
				catch
				{
					child = null;
				}
				if (child != null)
				{
					try
					{
						//he is in this family
						if (MyFamilies[child.FamilyID].Husband != "?")
						{
							fatherID = MyIndividuals[MyFamilies[child.FamilyID].Husband].ID;
							GetAncestors(fatherID, nextGeneration, fatherAncestorNumber);
						}
					}
					catch
					{
						//empty catch
					}
				}

				try
				{
					//he is in this family
					if (MyFamilies[child.FamilyID].Wife != "?")
					{
						motherID = MyIndividuals[MyFamilies[child.FamilyID].Wife].ID;
						GetAncestors(motherID, nextGeneration, motherAncestorNumber);
					}
				}
				catch
				{
					//empty catch
				}

				child = null;
			}

		}

		/// <summary>
		/// Method that builds one or more descendant outlines
		/// </summary>
		private void BuildDescendantOutlines()
		{
			//first check config settings and make sure they are valid
			//if not, either correct or abort.
			if (m_cfg.DotlIndividuals.Length == 0)
			{
				FireStatusMessage("No individuals listed for descendant outlines. Unable to continue.");
				return;
			}
			if (m_cfg.DotlNumberingStyle < 0 || m_cfg.DotlNumberingStyle > 2)
			{
				FireStatusMessage("Invalid numbering style reverting to numbering style 0. Valid values are 0 to 2.");
				m_cfg.DotlNumberingStyle = 0;
			}
			if (m_cfg.DotlNumGenerations < 1 || m_cfg.DotlNumGenerations > 15)
			{
				FireStatusMessage("Invalid number of generations reverting to 10. Valid values are 1 to 15.");
				m_cfg.DotlNumGenerations = 10;
			}

			//create an array of indviduals to process from
			//the comma-delimited list supplied in the config file
			char[] delims = new char[] { ',' };
			string[] individuals = new string[] { };

			individuals = m_cfg.DotlIndividuals.Split(delims);

			string id = "";

			//loop through individuals and create descendant charts for each one
			for (int i = 0; i < individuals.Length; i++)
			{
				//call into MakeDescendantOutline - a recursive function that operates on the individual(s) 
				//selected and their descendants.
				id = "@I" + individuals[i].Trim() + "@";
				StartOutline(id);
			}

		} //BuildDescendantOutlines

		private StringBuilder _sbDescendantOutline = null;

		///<summary>Start a descendant outline</summary>
		/// <param name="StartID">Indvidual who starts this outline</param>
		public void StartOutline(string StartID)
		{
			string file; //the web page file
			string link; //a local link to this file
			FileStream fs1;
			StreamWriter sw1;
			string q = "\""; //quote string

			//the StringBuilder that holds the HTML that gets written to the file
			_sbDescendantOutline = new StringBuilder();

			string rin = StartID.Replace("@", " ").Trim();
			//create the header for this page
			if (m_cfg.FamFilePrefix == "fam")
			{
				file = m_cfg.HtmlPath + "\\dotl_" + rin + _pext;
				link = "dotl_" + rin + _pext;
			}
			else
			{
				file = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_dotl_" + rin + _pext;
				link = m_cfg.FamFilePrefix + "_dotl_" + rin + _pext;
			}

			fs1 = new FileStream(file, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			string pageTitle = "";
			//get the persons name
			string personName = "";

			if (MyIndividuals[StartID].Title.Length != 0)
				personName = MyIndividuals[StartID].Title + " ";

			if (MyIndividuals[StartID].Forenames.Length != 0)
				personName += MyIndividuals[StartID].Forenames + " ";

			if (MyIndividuals[StartID].Surname.Length != 0)
				personName += MyIndividuals[StartID].Surname;

			if (MyIndividuals[StartID].Suffix.Length != 0)
				personName += ", " + MyIndividuals[StartID].Suffix;

			personName = personName.Trim();

			pageTitle = "Descendant Outline for " + personName;

			MyDescendantOutlines.Add(pageTitle, link, StartID);

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(file);
				_HtmlHelp.AddFileToContents(pageTitle, file);
			}

			WriteHtmlHeader(sw1, pageTitle);
			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + q + "center" + q + "><img src=" + q + m_cfg.BannerFile + q + "></p>");
			}

			sw1.WriteLine("<h1 align=\"center\">" + pageTitle + "</h1>");
			sw1.WriteLine(GetMenu());
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");


			//make the body of the descendant outline
			MakeDescendantOutline(StartID, 0, "", 0);

			sw1.WriteLine(_sbDescendantOutline.ToString());

			//create the footer for this page
			WriteHtmlFooter(sw1);

			sw1.Close();
			fs1.Close();

		}

		/// <summary>
		/// Recursive function that creates the string output for the descendant outline
		/// </summary>
		/// <param name="ID">ID (RIN) of current individual</param>
		/// <param name="level">Current level of the descendant outline</param>
		/// <param name="parentItem">The outline item string of the parent</param>
		/// <param name="item">The item number for the current individual</param>
		private void MakeDescendantOutline(string ID, int level, string parentItem, int item)
		{
			if (level > m_cfg.DotlNumGenerations) return;

			string spaces = "";
			if (level > 0)
			{
				for (int i = 0; i < level; i++)
				{
					spaces += "&nbsp;&nbsp;";
				}
			}

			//get the persons name
			string personName = "";

			if (MyIndividuals[ID].Title.Length != 0)
				personName = MyIndividuals[ID].Title + " ";

			if (MyIndividuals[ID].Forenames.Length != 0)
				personName += MyIndividuals[ID].Forenames + " ";

			if (MyIndividuals[ID].Surname.Length != 0)
				personName += MyIndividuals[ID].Surname;

			if (MyIndividuals[ID].Suffix.Length != 0)
				personName += ", " + MyIndividuals[ID].Suffix;

			personName = personName.Trim();

			//handle different numbering systems
			string henrynumber = "";
			if (parentItem.Length != 0)
				switch (m_cfg.DotlNumberingStyle)
				{
					case 0: //d'Aboville
						parentItem = parentItem + "." + item.ToString();
						break;
					case 1: //henry numbers
						if (item > 9)
						{
							henrynumber = "(" + item.ToString() + ")";
						}
						else
						{
							henrynumber = item.ToString();
						}
						parentItem = parentItem + henrynumber;
						break;
					case 2: //modified henry numbers
						if (item > 9)
						{
							henrynumber = GetModifiedHenryNumber(item);
						}
						else
						{
							henrynumber = item.ToString();
						}
						parentItem = parentItem + henrynumber;
						break;
				}
			else if (item > 0)
				parentItem = item.ToString();

			string personInfo;
			personInfo = String.Format("({0}) ", GetSexString(MyIndividuals[ID].Sex));

			if (MyIndividuals[ID].BirthDateText != "")
			{
				personInfo += String.Format(", {0} {1}", _pinfoBorn, MyIndividuals[ID].BirthDateText);
			}

			if (MyIndividuals[ID].DeathDateText != "")
			{
				personInfo += String.Format(", {0} {1}", _pinfoDied, MyIndividuals[ID].DeathDateText);
			}

			string link = "";
			if (m_cfg.DotlSSLinks == true)
			{
				string rin = ID.Replace("@", " ").Trim();
				rin = rin.Replace("I", " ").Trim();
				int idNumber = System.Int32.Parse(rin);

				link = String.Format("<%=Page.PersonHREF({0})%>", idNumber);
			}
			else
			{
				link = MyIndividuals[ID].LinkPage;
			}

			if (parentItem.Length != 0)
			{
				_sbDescendantOutline.Append(String.Format("<p>{0}<b>{1}</b> <a href=\"{2}\">{3}</a> - {4}</p>\r\n",
					spaces, parentItem, link, personName, personInfo));
			}
			else
			{
				_sbDescendantOutline.Append(String.Format("<p>{0}<a href=\"{1}\">{2}</a> - {3}</p>\r\n", 
					spaces, link, personName, personInfo));
			}

			int childItem = 0;

			for (int i = 0; i <= MyIndividuals[ID].FamList.Count - 1; i++)
			{
				if (MyFamilies[MyIndividuals[ID].FamList[i].FamId].HasChildren == true)
				{
					foreach (Child child in MyChildren)
					{
						if (child.FamilyID == MyIndividuals[ID].FamList[i].FamId)
						{
							childItem += 1;
							MakeDescendantOutline(child.ChildID, level + 1, parentItem, childItem);
						}
					}
				}
			}
		}

		private string GetModifiedHenryNumber(int item)
		{
			string henryNumber = "";
			if (item == 10) henryNumber = "A";
			if (item == 11) henryNumber = "B";
			if (item == 12) henryNumber = "C";
			if (item == 13) henryNumber = "D";
			if (item == 14) henryNumber = "E";
			if (item == 15) henryNumber = "F";
			if (item == 16) henryNumber = "G";
			if (item == 17) henryNumber = "H";
			if (item == 18) henryNumber = "I";
			if (item == 19) henryNumber = "J";
			if (item == 20) henryNumber = "K";
			if (item == 21) henryNumber = "L";
			if (item == 22) henryNumber = "M";
			if (item == 23) henryNumber = "N";
			if (item == 24) henryNumber = "O";
			if (item == 25) henryNumber = "P";
			if (item == 26) henryNumber = "Q";
			if (item == 27) henryNumber = "R";
			if (item == 28) henryNumber = "S";
			if (item == 29) henryNumber = "T";
			if (item == 30) henryNumber = "U";
			if (item == 31) henryNumber = "V";
			if (item == 32) henryNumber = "W";
			if (item == 33) henryNumber = "X";
			if (item == 34) henryNumber = "Y";
			if (item == 35) henryNumber = "Z";
			return henryNumber;
		}

		/// <summary>
		/// Copies the CSS file to the HTML folder
		/// </summary>
		private void CopyCssFile()
		{
			try
			{
				_cssFile = Path.GetFileName(m_cfg.CSSFile);

				if (File.Exists(m_cfg.HtmlPath + "\\" + _cssFile))
				{
					File.Delete(m_cfg.HtmlPath + "\\" + _cssFile);
				}

				File.Copy(m_cfg.CSSFile, m_cfg.HtmlPath + "\\" + _cssFile);

			}
			catch (Exception ex)
			{
				_cssFile = "";
				FireErrorMessage("Erroring while copying " + m_cfg.CSSFile + ": " + ex.Message);
			}
		}

		/// <summary>
		/// Builds an RSS feed using the RSS Utilities library
		/// </summary>
		private void BuildRssFeed()
		{
			try
			{
				string path = m_cfg.HtmlPath + "\\rssfeed.xml";
				System.DateTime DaysAgo90 = DateTime.Now.AddDays(-m_cfg.DaysRecent);

				// Delete the file if it exists.
				if (File.Exists(path))
				{
					File.Delete(path);
				}

				System.IO.FileStream outputStream = File.Create(path);

				IranianExperts.RSS.RSSChannel oRSSChannel =
					new IranianExperts.RSS.RSSChannel(m_cfg.MainPageTitle, m_cfg.AuthorsUrl, m_cfg.RssDescription);

				oRSSChannel.PubDate = System.DateTime.Now.ToString();

				oRSSChannel.Generator = "UncleGed - GEDCOM-to-HTML software ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00");
				oRSSChannel.WebMaster = m_cfg.AuthorsEmail;
				oRSSChannel.Copyright = "Copyright &copy; " + DateTime.Now.Year.ToString() + "&nbsp;" + m_cfg.AuthorsName;


				IranianExperts.RSS.RSSRoot oRSSRoot = new IranianExperts.RSS.RSSRoot(oRSSChannel, outputStream);
				IranianExperts.RSS.RSSItem oRSSItem = null;

				string title = "";
				string url = "";
				string description = "";
				bool hasSources = false;
				bool hasNotes = false;


				//loop through individuals and add RSS items
				foreach (Individual i in MyIndividuals)
				{
					//only add them if they are recent
					if (i.LastChangeDate > DaysAgo90)
					{
						title = String.Format("{0}, {1} ({2}-{3})", i.Surname, i.Forenames, i.BirthDateText, i.DeathDateText);
						if (_RssForSS == false)
						{
							url = m_cfg.RssRootURL + "/" + i.LinkPage;
						}
						else
						{
							url = m_cfg.RssRootURL + "/p" + i.IdNumber.ToString() + ".htm";
						}

						oRSSItem = new IranianExperts.RSS.RSSItem(title, url);
						oRSSItem.PubDate = i.LastChangeDate.ToString();
						oRSSItem.Guid = url;

						if (i.Sources.Count > 0)
							hasSources = true;
						else
							hasSources = false;

						if (i.Notes.Length > 0)
							hasNotes = true;
						else
							hasNotes = false;

						description = string.Format("RIN:{0} DOB:{1} POB:{2} DOD:{3} POD:{4} SRCS:{5} NTS:{6}",
							i.ID, i.BirthDateText, i.BirthPlace, i.DeathDateText, i.DeathPlace,
							hasSources, hasNotes);

						oRSSItem.Description = description;

						oRSSRoot.Items.Add(oRSSItem);

					}
				}

				IranianExperts.RSS.RSSUtilities.PublishRSS(oRSSRoot);
			}
			catch (Exception ex)
			{
				FireErrorMessage("Erroring while creating RSS feed: " + ex.Message);
			}
		}

		/// <summary>
		/// Builds Person pages - added in 9.0
		/// </summary>
		/// <returns>false if fails</returns>
		private bool BuildPersonPages()
		{
			int pageCount = 0;
			int indiCount = 0;
			int totCount = 0;
			string cleanTemplate = "";
			string currentTemplate = "";
			string personBlock = "";
			string indiBlock = "";
			string line = "";
			string rin = "";
			string childInfo = "";
			string CRLF = "\r\n";
			string title = "";
			string personName = "";

			try
			{
				cleanTemplate = GetPersonPageTemplate();

				currentTemplate = cleanTemplate;

				//map the location of each individual
				foreach (Individual individual in MyIndividuals)
				{
					if (indiCount >= m_cfg.MaxIndPerPage || MyIndividuals.Count == totCount)
					{
						pageCount += 1;
						indiCount = 0;
					}

					indiCount++;
					totCount++;

					if (pageCount == 0) //start a new page
					{
						pageCount = 1;
					}

					rin = individual.ID.Replace("@", " ").Trim();
					individual.LinkPage = String.Format("p{0}{1}#{2}", pageCount, _pext, rin);

				}

				pageCount = 0;
				indiCount = 0;
				totCount = 0;

				//if no birth or death then don't display

				foreach (Individual individual in MyIndividuals)
				{
					indiCount++;
					totCount++;
					indiBlock = "";

					if (pageCount == 0) //start a new page
					{
						pageCount = 1;
					}

					rin = individual.ID.Replace("@", " ").Trim();
					line = String.Format("<hr /><div class=\"indsection\" id=\"{0}\">", rin);
					indiBlock += line + CRLF;

					personName = "";

					if (individual.Title.Length != 0)
						personName = individual.Title + " ";

					if (individual.Forenames.Length != 0)
						personName += individual.Forenames + " ";

					if (individual.Surname.Length != 0)
						personName += individual.Surname;

					if (individual.Suffix.Length != 0)
						personName += ", " + individual.Suffix;

					personName = personName.Trim();

					line = String.Format("<h2 class=\"indname\">{0}</h2>", personName);

					//if (individual.Suffix != "")
					//   line = String.Format("<h2 class=\"indname\">{0}</h2>", individual.Forenames + " " + individual.Surname + ", " + individual.Suffix);
					//else
					//   line = String.Format("<h2 class=\"indname\">{0}</h2>", individual.Forenames + " " + individual.Surname);

					indiBlock += line + CRLF;

					//display the person info line
					line = String.Format("<div class=\"indinfo\">{0}, #{1}",
						GetSexString(individual.Sex), rin);

					if (individual.BirthDateText != "")
					{
						line += String.Format(", {0} {1}", _pinfoBorn, individual.BirthDateText);
					}

					if (individual.DeathDateText != "")
					{
						line += String.Format(", {0} {1}", _pinfoDied, individual.DeathDateText);
					}

					line += "</div>";
					indiBlock += line + CRLF;

					//parents
					line = "<ul class=\"bullet\">";
					indiBlock += line + CRLF;
					line = GetFather(individual);
					indiBlock += line + CRLF;
					line = GetMother(individual);
					indiBlock += line + CRLF;
					line = "</ul>";
					indiBlock += line + CRLF;

					//events
					//string occupation = "";

					if (individual.LifeEvents.IncludedCount != 0 && !individual.IsPrivate)
					{
						line = "<ul class=\"bullet\">";
						indiBlock += line + CRLF;
						foreach (LifeEvent ev in individual.LifeEvents)
						{
							if (ev.EventText.Trim() != "Notes" && ev.EventText.Trim() != "!")
							{
								line = String.Format("<li><em>{0}</em>: {1} {2} {3} {4}</li>",
									ev.EventText,
									ev.EventDescription,
									ev.EventDateString,
									ev.EventPlace,
									ev.EventNote);

								indiBlock += line + CRLF;
							}
						}
						line = "</ul>";
						indiBlock += line + CRLF;
					}

					line = "</div>";
					indiBlock += line + CRLF;

					//GENDEX
					if (m_cfg.CreateGendexFile == true)
					{
						//"GENDEX.txt"
						string gendexUrl = individual.LinkPage;

						MyGendices.Add(gendexUrl,
							sf.UCase(individual.Surname),
							individual.Forenames + " /" + sf.UCase(individual.Surname) + "/",
							(individual.BirthDateText),
							(individual.BirthPlace),
							(individual.DeathDateText),
							(individual.DeathPlace),
							CreateGUIDKey());
					}


					//marriages
					string spouseID = "";
					Individual spouse;
					string spouseName = "";
					string spouseLink = "";
					string spouseBirth = "";
					string spouseDeath = "";
					string marriageDate = "";
					string marriagePlace = "";

					if (individual.FamList.Count > 0)
					{
						line = "<div class=\"familysection\">";
						indiBlock += line + CRLF;
						line = String.Format("<h3>{0}</h3>", m_cfg.PersonPages.FamilyString);
						indiBlock += line + CRLF;

						for (int i = 0; i <= individual.FamList.Count - 1; i++)
						{
							spouseID = "";
							spouseLink = "";

							marriageDate = "";
							marriagePlace = "";

							if (MyFamilies[individual.FamList[i].FamId].MarriageDateText != "")
								marriageDate = MyFamilies[individual.FamList[i].FamId].MarriageDateText;

							if (MyFamilies[individual.FamList[i].FamId].MarriagePlace != "")
								marriagePlace = MyFamilies[individual.FamList[i].FamId].MarriagePlace;


							if (individual.Sex == "M")
							{
								spouseID = MyFamilies[individual.FamList[i].FamId].Wife;
							}
							else
							{
								spouseID = MyFamilies[individual.FamList[i].FamId].Husband;
							}

							if (spouseID != "?")
							{
								spouse = MyIndividuals[spouseID];
								spouseLink = spouse.LinkPage;
								spouseName = spouse.Forenames + " " + spouse.Surname;
								spouseBirth = spouse.BirthDateText;
								spouseDeath = spouse.DeathDateText;
								if (spouse.Suffix != "")
								{
									spouseName += ", " + spouse.Suffix;
								}
							}
							else
							{
								spouseName = "UNKNOWN";
							}

							//spouse
							if (spouseName != "UNKNOWN")
							{
								line = String.Format("<p><em>{0} {1} </em>: <a href=\"{2}\">{3}</a> ",
									m_cfg.PersonPages.MarriageString,
									(i + 1),
									spouseLink,
									spouseName);

								if (marriageDate != "" && !individual.IsPrivate)
								{
									line += String.Format("{0} {1}", _pinfoMarried, marriageDate);

									if (marriagePlace != "")
										line += String.Format(" {0}", marriagePlace);
								}

								if (spouseBirth != "")
								{
									line += String.Format(", {0} {1}", _pinfoBorn, spouseBirth);
								}

								if (spouseDeath != "")
								{
									line += String.Format(", {0} {1}", _pinfoDied, spouseDeath);
								}


								line += "</p>";
							}
							else
							{
								line = String.Format("<p><em>{0} {1} </em>: {2}</p>",
									m_cfg.PersonPages.MarriageString,
									(i + 1),
									spouseName
									);
							}
							indiBlock += line + CRLF;

							if (MyFamilies[individual.FamList[i].FamId].HasChildren == true)
							{
								line = "<ol>";
								indiBlock += line + CRLF;

								foreach (Child child in MyChildren)
								{
									//TODO: need to order children
									if (child.FamilyID == individual.FamList[i].FamId)
									{
										childInfo = GetChild(child.ChildID);
										line = String.Format("<li>{0}</li>", childInfo);
										indiBlock += line + CRLF;
									}
								}
								line = "</ol>";
								indiBlock += line + CRLF;
							}

						}
						line = "</div>";
						indiBlock += line + CRLF;
					}

					//notes:
					if (individual.Notes != "")
					{
						line = String.Format("<p><em>{0}:</em></p>", m_cfg.PersonPages.NotesString);
						indiBlock += line + CRLF;

						foreach (Note note in MyNotes)
						{
							if (note.ID == individual.Notes)
							{
								line = String.Format("<blockquote>{0}</blockquote>", note.NoteText);
								indiBlock += line + CRLF;
							}
						}
					}

					//adding a pedigree
					line = "";
					if (m_cfg.AddPedigreeToPerson == true)
					{
						line = String.Format("<h3>{0}:</h3>", m_cfg.PersonPages.PedigreeString);
						indiBlock += line + CRLF;

						line = GetPedigreeSection(individual);
						if (line != "") indiBlock += line + CRLF;
					}

					//sources for individual
					int numSrcs;
					numSrcs = individual.Sources.Count;
					line = "";

					if (numSrcs > 0)
					{
						line += String.Format("<div class=\"sources\">");
						line += String.Format("<p>{0}</p>", m_cfg.PersonPages.SourcesString);
					}


					if (numSrcs > 0)
					{
						line += String.Format("<ol>");
						foreach (Source src in individual.Sources)
						{
							try
							{
								line += String.Format("<li>");
								if (m_cfg.FamFilePrefix == "fam")
								{
									line += String.Format("<em>" + "<a href=\"sources" + _pext + "#" + src.ID + "\"" + " target=" + "\"" + _srcTarget + "\"" + ">" + MySources[src.ID].Title + "</a>, " + "</em>" + src.Page);
								}
								else
								{
									line += String.Format("<em>" + "<a href=" + "\"" + m_cfg.FamFilePrefix + "_sources" + _pext + "#" + src.ID + "\"" + " target=" + "\"" + _srcTarget + "\"" + ">" + MySources[src.ID].Title + "</a>, " + "</em>" + src.Page);
								}
								if (src.Note != "")
								{
									line += String.Format(src.Note + "&nbsp;");
								}
								if (src.SourceText != "")
								{
									line += String.Format("<em>" + src.SourceText + "</em>");
								}
								line += String.Format("</li>");
							}
							catch
							{
								//empty catch to get around problem with reading ROOTS III gedcom
							}
						} // src
						line += String.Format("</ol>");
						line += String.Format("</div>");
					}

					if (line != "") indiBlock += line + CRLF;

					//display the last changed date
					line = "<p align=\"center\">";
					if (m_cfg.PersonPages.LastUpdatedDisplay == true && individual.LastChangeDate.CompareTo(_newDate) != 0)
					{
						line += String.Format("{0}: {1}<br />",
							m_cfg.PersonPages.LastUpdatedString,
							individual.LastChangeDate.ToString("dd MMMM yyyy"));
					}

					line += "<a href=\"#TOP\">[TOP]</a></p>";
					indiBlock += line + CRLF;

					personBlock += indiBlock;

					if (indiCount >= m_cfg.MaxIndPerPage || MyIndividuals.Count == totCount)
					{
						title = m_cfg.MainPageTitle + "-" + m_cfg.PersonPages.PageTitleString.Replace("[N]", pageCount.ToString());
						currentTemplate = currentTemplate.Replace("<%WEB_PAGE_TITLE%>", title);


						title = m_cfg.PersonPages.PageTitleString.Replace("[N]", pageCount.ToString());
						currentTemplate = currentTemplate.Replace("<%PAGE_TITLE%>", title);

						currentTemplate = currentTemplate.Replace("<%PERSON_BLOCK%>", personBlock);

						string outFile = m_cfg.HtmlPath + "\\" + "p" + pageCount.ToString() + _pext;
						FireStatusMessage("Building Person Page " + UncleGED.Utils.Tools.LongDirFix(outFile, 32) + "...");
						//now write it out
						//need to figure out the correct encoding method
						using (StreamWriter sw = new StreamWriter(outFile, false, Encoding.UTF8))
						{
							sw.Write(currentTemplate);
							sw.Close();
						}

						currentTemplate = cleanTemplate;
						indiCount = 0;
						personBlock = "";
						pageCount += 1;
					}
				}

				return true;
			}
			catch (Exception ex)
			{
				FireStatusMessage("Error while building person pages. Error: " + ex.ToString());
				return false;
			}
		}

		/// <summary>
		/// Returns a pedigree table section for the page
		/// </summary>
		/// <param name="individual"></param>
		/// <returns>formatted ouput</returns>
		private string GetPedigreeSection(Individual individual)
		{
			string template = "<div class=\"pedigree\"> <table class=\"pedigreetable\"> <tr style=\"height: 14.5pt\"> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %PATERNAL_GRANDFATHER% </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %FATHER% </p> </td> <td style=\"border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %PATERNAL_GRANDMOTHER% </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %INDIVIDUAL% </p> </td> <td style=\"border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %MATERNAL_GRANDFATHER% </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %MOTHER% </p> </td> <td style=\"border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> </tr> <tr style=\"height: 14.5pt\"> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> &nbsp; </p> </td> <td style=\" padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: right; line-height: normal;\"> &nbsp; </p> </td> <td style=\"padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; text-align: center; line-height: normal;\"> | </p> </td> <td style=\" border: none; border-bottom: solid windowtext 1.0pt; padding: 0in 5.4pt 0in 5.4pt; height: 14.5pt\"> <p style=\"margin-bottom: 0in; margin-bottom: .0001pt; line-height: normal;\"> %MATERNAL_GRANDMOTHER% </p> </td> </tr> </table> </div>";
			string indiName = "";
			string fatherName = "{UNKNOWN}";
			string motherName = "{UNKNOWN}";
			string patgfName = "{UNKNOWN}";
			string patgmName = "{UNKNOWN}";
			string matgfName = "{UNKNOWN}";
			string matgmName = "{UNKNOWN}";
			Individual parent = null;
			Individual temp = null;

			if (individual.Suffix != "")
				indiName = String.Format("{0}", individual.Forenames + " " + individual.Surname + ", " + individual.Suffix);
			else
				indiName = String.Format("{0}", individual.Forenames + " " + individual.Surname);

			//get the person's father's link
			fatherName = GetFatherLink(individual, ref parent);
			//paternal grandparents
			if (parent != null)
			{
				patgfName = GetFatherLink(parent, ref temp);
				patgmName = GetMotherLink(parent, ref temp);
			}

			//get the person's mother's link
			motherName = GetMotherLink(individual, ref parent);
			//paternal grandparents
			if (parent != null)
			{
				matgfName = GetFatherLink(parent, ref temp);
				matgmName = GetMotherLink(parent, ref temp);
			}


			template = template.Replace("%INDIVIDUAL%", indiName);
			template = template.Replace("%FATHER%", fatherName);
			template = template.Replace("%PATERNAL_GRANDFATHER%", patgfName);
			template = template.Replace("%PATERNAL_GRANDMOTHER%", patgmName);
			template = template.Replace("%MOTHER%", motherName);
			template = template.Replace("%MATERNAL_GRANDFATHER%", matgfName);
			template = template.Replace("%MATERNAL_GRANDMOTHER%", matgmName);

			return template;
		}

		/// <summary>
		/// Returns the string male or female
		/// </summary>
		private string GetSexString(string p)
		{
			if (p == "M") return m_cfg.PersonPages.MaleString;
			if (p == "F") return m_cfg.PersonPages.FemaleString;
			if (p == "U") return "?";

			return "?";
		}

		/// <summary>
		/// returns the template for a person page
		/// </summary>
		/// <returns></returns>
		private string GetPersonPageTemplate()
		{
			StringBuilder sb = new StringBuilder();
			string QUOTE = "\"";
			string CRLF = "\r\n";

			//rewritten to reflect changes made to html in all other pages

			//file header
			sb.Append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" + CRLF);
			sb.Append("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">");
			//sb.Append("<HTML>" + CRLF);
			sb.Append("<head>" + CRLF);
			sb.Append("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>" + CRLF);
			//if (m_cfg.UseCSS == true) sb.Append("<link rel=\"stylesheet\" type=\"text/css\" href=\"uged.css\"/>" + CRLF);
			sb.Append("<title><%WEB_PAGE_TITLE%></title>" + CRLF);
			sb.Append("<meta name=\"Generator\" content=\"UncleGed Version " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00") + "\"/>" + CRLF);

			if (m_cfg.CreateRssFeed == true)
			{
				string rsslink =
					String.Format("<link rel=\"alternate\" type=\"application/rss+xml\" href=\"{0}/rssfeed.xml\" title=\"{1} RSS Feed\" />",
					m_cfg.RssRootURL, m_cfg.MainPageTitle);

				sb.Append(rsslink + CRLF);
			}

			if (m_cfg.CSSOption == 1)
			{
				sb.Append("<link rel = \"stylesheet\" type = \"text/css\" href=\"uged.css\"/>" + CRLF);
			}
			else if (m_cfg.CSSOption == 2 && _cssFile.Length != 0)
			{
				sb.Append(String.Format("<link rel = \"stylesheet\" type = \"text/css\" href=\"{0}\"/>", _cssFile) + CRLF);
			}


			sb.Append("</head>" + CRLF);

			if (m_cfg.UseBackground == true && m_cfg.CSSOption == 0)
			{
				sb.Append("<body style=\"background-image: url('" + m_cfg.BackgroundFile + "');\">" + CRLF);
			}
			else if (m_cfg.CSSOption == 1 || m_cfg.CSSOption == 2)
			{
				sb.Append("<body>" + CRLF);
			}
			else
			{
				sb.Append("<body link=\"Blue\" vlink=\"Purple\">" + CRLF);
			}


			if (m_cfg.UseBanner)
			{
				sb.Append("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>" + CRLF);
			}

			sb.Append("<a name=\"TOP\"></a>" + CRLF);
			sb.Append("<h1 align=\"center\"><%PAGE_TITLE%></h1>" + CRLF);

			sb.Append(GetMenu());

			sb.Append("<%PERSON_BLOCK%>" + CRLF);

			sb.Append("<hr size=\"1\" noshade=\"noshade\"/>" + CRLF);
			sb.Append("<div class=\"copyright\">" + CRLF);
			if (m_cfg.AddUserCopyright == true && m_cfg.AuthorsName != "")
			{
				sb.Append("<p style=\"text-align: center; font-size:smaller; font-style: italic;\">" + CRLF);
				sb.Append("Copyright &copy; " + DateTime.Now.Year.ToString() + "&nbsp;" + CRLF);
				if (m_cfg.AuthorsEmail != "") sb.Append("<a href=" + QUOTE + "mailto:" + m_cfg.AuthorsEmail + QUOTE + ">");
				sb.Append(m_cfg.AuthorsName);
				if (m_cfg.AuthorsEmail != "") sb.Append("</a>" + CRLF);
				sb.Append("</p>" + CRLF);
			}

			sb.Append("<p style=\"text-align: center; font-size:smaller; font-style: italic;\">" + CRLF);
			sb.Append("This page was created on " + DateTime.Now.ToString("dd MMMM yyyy") + " using <a href=" + QUOTE + "http://gatheringleaves.org/uged" + QUOTE + " target=_blank>UncleGed - GEDCOM-to-HTML software</a>  ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00"));
			sb.Append(" - Copyright &copy; 1999-2007 D. M. DeBacker");
			sb.Append("</p>" + CRLF);
			sb.Append("</div>" + CRLF);

			sb.Append("</body>" + CRLF);
			sb.Append("</html>" + CRLF);

			return sb.ToString();
		}

		/// <summary>
		/// Gets the menu
		/// </summary>
		/// <returns>menu string</returns>
		private string GetMenu()
		{
			StringBuilder sb = new StringBuilder();
			string CRLF = "\r\n";

			//CHANGE: menu dynamic based on the options selected

			//add menu to page
			sb.Append("            <div align=\"center\">" + CRLF);
			sb.Append("                <table border=\"1\" cellpadding=\"0\" cellspacing=\"0\" style=\"width: 100%\">" + CRLF);
			sb.Append("                    <tr>" + CRLF);

			//changed so that AuthorsURL can be substituted for index.htm 
			sb.Append("                        <td align=\"center\">" + CRLF);
			if (m_cfg.HomeEqualsHomePage == false)
			{
				sb.Append("                            <a href=\"index" + _pext + "\">Home</a>" + CRLF);
			}
			else
			{
				sb.Append("                            <a href=\"" + m_cfg.AuthorsUrl + "\">Home</a>" + CRLF);
			}
			sb.Append("                        </td>" + CRLF);

			sb.Append("                        <td align=\"center\">" + CRLF);
			if (m_cfg.FamFilePrefix == "fam")
			{
				sb.Append("                            <a href=\"ind_idx" + _pext + "\">Index</a>" + CRLF);

			}
			else
			{
				//" + m_cfg.FamFilePrefix + "
				sb.Append("                            <a href=\"" + m_cfg.FamFilePrefix + "_ind_idx" + _pext + "\">Index</a>" + CRLF);
			}
			sb.Append("                        </td>                        " + CRLF);

			sb.Append("                        <td align=\"center\">" + CRLF);
			if (m_cfg.FamFilePrefix == "fam")
			{

				sb.Append("                            <a href=\"surnames" + _pext + "\">Surnames</a>" + CRLF);
			}
			else
			{
				sb.Append("                            <a href=\"" + m_cfg.FamFilePrefix + "_surnames" + _pext + "\">Surnames</a>" + CRLF);
			}
			sb.Append("                        </td>" + CRLF);

			if (m_cfg.CreateStatsPage == true)
			{
				sb.Append("                        <td align=\"center\">" + CRLF);
				if (m_cfg.FamFilePrefix == "fam")
				{
					sb.Append("                            <a href=\"stats" + _pext + "\">Stats</a>" + CRLF);
				}
				else
				{
					sb.Append("                            <a href=\"" + m_cfg.FamFilePrefix + "_stats" + _pext + "\">Stats</a>" + CRLF);
				}
				sb.Append("                        </td>" + CRLF);
			}

			if (m_cfg.CreateChronoPage == true)
			{
				sb.Append("                        <td align=\"center\">" + CRLF);
				if (m_cfg.FamFilePrefix == "fam")
				{
					sb.Append("                            <a href=\"chrono" + _pext + "\">Chrono</a>" + CRLF);
				}
				else
				{
					sb.Append("                            <a href=\"" + m_cfg.FamFilePrefix + "_chrono" + _pext + "\">Chrono</a>" + CRLF);
				}
				sb.Append("                        </td>" + CRLF);
			}

			if (m_cfg.CreateLocPage == true)
			{
				sb.Append("                        <td align=\"center\">" + CRLF);
				if (m_cfg.FamFilePrefix == "fam")
				{
					sb.Append("                            <a href=\"locations" + _pext + "\">Locations</a>" + CRLF);
				}
				else
				{
					sb.Append("                            <a href=\"" + m_cfg.FamFilePrefix + "_locations" + _pext + "\">Locations</a>" + CRLF);
				}
				sb.Append("                        </td>" + CRLF);
			}

			if (m_cfg.CreateOccupationsPage == true)
			{
				sb.Append("                        <td align=\"center\">" + CRLF);
				if (m_cfg.FamFilePrefix == "fam")
				{
					sb.Append("                            <a href=\"occupations" + _pext + "\">Occupations</a>" + CRLF);
				}
				else
				{
					sb.Append("                            <a href=\"" + m_cfg.FamFilePrefix + "_occupations" + _pext + "\">Occupations</a>" + CRLF);
				}
				sb.Append("                        </td>" + CRLF);
			}

			sb.Append("                    </tr>" + CRLF);
			sb.Append("                </table>" + CRLF);
			sb.Append("            </div>" + CRLF);

			return sb.ToString();
		}

		/// <summary>
		/// returns a child string
		/// </summary>
		/// <param name="p"></param>
		/// <returns>child string</returns>
		private string GetChild(string p)
		{
			string childInfo = "";
			string childName = "";

			if (MyIndividuals[p].Suffix == "")
			{
				childName = MyIndividuals[p].Forenames + " " + MyIndividuals[p].Surname;
			}
			else
			{
				childName = MyIndividuals[p].Forenames + " " + MyIndividuals[p].Surname + ", " + MyIndividuals[p].Suffix;
			}

			if (MyIndividuals[p].HasOwnFamily == true)
			{
				//&sect;
				childInfo = String.Format("{0}&nbsp;<a href=\"{1}\">{2}</a>",
					m_cfg.MarkForMoreLinks,
					MyIndividuals[p].LinkPage,
					childName);
			}
			else
			{
				childInfo = String.Format("&nbsp;&nbsp;&nbsp;<a href=\"{0}\">{1}</a>",
					MyIndividuals[p].LinkPage,
					childName);
			}

			if (MyIndividuals[p].BirthDateText != "")
			{
				childInfo += String.Format(", {0} {1}", _pinfoBorn, MyIndividuals[p].BirthDateText);
			}

			if (MyIndividuals[p].DeathDateText != "")
			{
				childInfo += String.Format(", {0} {1}", _pinfoDied, MyIndividuals[p].DeathDateText);
			}


			return childInfo;
		}

		/// <summary>
		/// get the mother info for the current individual
		/// </summary>
		/// <param name="individual">indi object</param>
		/// <returns>mother info</returns>
		private string GetMother(Individual individual)
		{
			string famKey = individual.FamilyID + individual.ID;
			string mother = String.Format("<li><em>{0}</em>: ?</li>", m_cfg.PersonPages.MotherString);
			string motherName = "";

			{
				Child child;
				try
				{
					child = MyChildren[famKey];
				}
				catch
				{
					child = null;
				}
				if (child != null)
				{
					try
					{
						//he is in this family
						if (MyFamilies[child.FamilyID].Wife != "?")
						{
							motherName = MyIndividuals[MyFamilies[child.FamilyID].Wife].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Wife].Surname;

							mother = String.Format("<li><em>{0}</em>: <a href=\"{1}\">{2}</a>",
								m_cfg.PersonPages.MotherString,
								MyIndividuals[MyFamilies[child.FamilyID].Wife].LinkPage,
								motherName);

							if (MyIndividuals[MyFamilies[child.FamilyID].Wife].BirthDateText != "")
							{
								mother += String.Format(", {0} {1}", _pinfoBorn, MyIndividuals[MyFamilies[child.FamilyID].Wife].BirthDateText);
							}

							if (MyIndividuals[MyFamilies[child.FamilyID].Wife].DeathDateText != "")
							{
								mother += String.Format(", {0} {1}", _pinfoDied, MyIndividuals[MyFamilies[child.FamilyID].Wife].DeathDateText);
							}

							mother += "</li>";
						}
					}
					catch
					{
						mother = String.Format("<li><em>{0}</em>: ?</li>", m_cfg.PersonPages.MotherString);
					}
				}
				child = null;
			}

			return mother;
		}

		/// <summary>
		/// returns a link for the individual's mother
		/// </summary>
		/// <param name="individual"></param>
		/// <param name="parent">holds the parent of this indvidual</param>
		/// <returns></returns>
		private string GetMotherLink(Individual individual, ref Individual parent)
		{
			string famKey = individual.FamilyID + individual.ID;
			string motherLink = "{UNKNOWN}";
			string motherName = "";
			parent = null;

			{
				Child child;
				try
				{
					child = MyChildren[famKey];
				}
				catch
				{
					child = null;
				}
				if (child != null)
				{
					try
					{
						//he is in this family
						if (MyFamilies[child.FamilyID].Wife != "?")
						{
							motherName = MyIndividuals[MyFamilies[child.FamilyID].Wife].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Wife].Surname;

							motherLink = String.Format("<a href=\"{0}\">{1}</a>",
								MyIndividuals[MyFamilies[child.FamilyID].Wife].LinkPage,
								motherName);

							parent = MyIndividuals[MyFamilies[child.FamilyID].Wife];
						}
					}
					catch
					{
						motherLink = "{UNKNOWN}";
					}
				}
				child = null;
			}

			return motherLink;
		}

		/// <summary>
		/// returns a link for the individual's Father
		/// </summary>
		/// <param name="individual"></param>
		/// <param name="parent">holds the parent of this indvidual</param>
		/// <returns></returns>
		private string GetFatherLink(Individual individual, ref Individual parent)
		{
			string famKey = individual.FamilyID + individual.ID;
			string fatherLink = "{UNKNOWN}";
			string fatherName = "";
			parent = null;

			{
				Child child;
				try
				{
					child = MyChildren[famKey];
				}
				catch
				{
					child = null;
				}
				if (child != null)
				{
					try
					{
						//he is in this family
						if (MyFamilies[child.FamilyID].Husband != "?")
						{
							fatherName = MyIndividuals[MyFamilies[child.FamilyID].Husband].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Husband].Surname;

							fatherLink = String.Format("<a href=\"{0}\">{1}</a>",
								MyIndividuals[MyFamilies[child.FamilyID].Husband].LinkPage,
								fatherName);

							parent = MyIndividuals[MyFamilies[child.FamilyID].Husband];
						}
					}
					catch
					{
						fatherLink = "{UNKNOWN}";
					}
				}
				child = null;
			}

			return fatherLink;
		}


		/// <summary>
		/// Returns a string for the individuals father
		/// </summary>
		/// <param name="individual">the individual object</param>
		/// <returns>father string</returns>
		private string GetFather(Individual individual)
		{
			string famKey = individual.FamilyID + individual.ID;
			string father = String.Format("<li><em>{0}</em>: ?</li>", m_cfg.PersonPages.FatherString);
			string fatherName = "";

			{
				Child child;
				try
				{
					child = MyChildren[famKey];
				}
				catch
				{
					child = null;
				}
				if (child != null)
				{
					try
					{
						//he is in this family
						if (MyFamilies[child.FamilyID].Husband != "?")
						{
							fatherName = MyIndividuals[MyFamilies[child.FamilyID].Husband].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Husband].Surname;

							father = String.Format("<li><em>{0}</em>: <a href=\"{1}\">{2}</a>",
								m_cfg.PersonPages.FatherString,
								MyIndividuals[MyFamilies[child.FamilyID].Husband].LinkPage,
								fatherName);

							if (MyIndividuals[MyFamilies[child.FamilyID].Husband].BirthDateText != "")
							{
								father += String.Format(", {0} {1}", _pinfoBorn, MyIndividuals[MyFamilies[child.FamilyID].Husband].BirthDateText);
							}

							if (MyIndividuals[MyFamilies[child.FamilyID].Husband].DeathDateText != "")
							{
								father += String.Format(", {0} {1}", _pinfoDied, MyIndividuals[MyFamilies[child.FamilyID].Husband].DeathDateText);
							}

							father += "</li>";
						}
					}
					catch
					{
						father = String.Format("<li><em>{0}</em>: ?</li>", m_cfg.PersonPages.FatherString);
					}
				}
				child = null;
			}

			return father;
		}

		#endregion

		#region Page Building methods

		/// <summary>
		/// Summary of BuildOccupationsReport.
		/// </summary>
		/// 		
		private void BuildOccupationsReport()
		{
			string sFile;
			int iOccCount;
			int iNumOcc;
			string sCurrOcc;
			string sPrevOcc;
			string sOccupation;
			string sFile2;
			const char QUOTE = '"';

			//build a list of occupations
			GetOccupations();
			iNumOcc = MyOccupations.Count;

			//if (iNumOcc == 0) return;

			SortedList sl = new SortedList(iNumOcc);
			sl.Clear();

			for (iOccCount = 0; iOccCount <= iNumOcc - 1; iOccCount++)
			{
				sl.Add(MyOccupations[iOccCount].OccupationTitle + CreateGUIDKey(), iOccCount);
			} // iOccCount

			if (m_cfg.FamFilePrefix == "fam")
			{
				sFile = m_cfg.HtmlPath + "\\occupations" + _pext;
			}
			else
			{
				sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_occupations" + _pext;
			}

			if (m_cfg.DoCompileHtml == true)
			{
				_HtmlHelp.AddFileToProject(sFile);
				_HtmlHelp.AddFileToContents("Occupations", sFile);
			}

			FileStream fs1 = new FileStream(sFile, FileMode.Create);
			StreamWriter sw1 = new StreamWriter(fs1);

			sFile2 = m_cfg.HtmlPath + "\\occ_report.txt";

			FileStream fs2 = new FileStream(sFile2, FileMode.Create);
			StreamWriter sw2 = new StreamWriter(fs2);

			//file header
			WriteHtmlHeader(sw1, m_cfg.OccupationsPageTitle);
			//sw1.WriteLine("<HTML>");
			//sw1.WriteLine("<HEAD>");
			//sw1.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">");
			//if (m_cfg.UseCSS == true) sw1.WriteLine("<LINK REL = stylesheet TYPE = text/css href=uged.css>");
			//sw1.WriteLine("<TITLE>" + m_cfg.OccupationsPageTitle + "</TITLE>");
			//sw1.WriteLine("<META NAME=" + QUOTE + " Generator" + QUOTE + " CONTENT=" + QUOTE + " UncleGed Version " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00") + QUOTE + ">");
			//sw1.WriteLine("</HEAD>");

			//if (m_cfg.UseBackground)
			//{
			//   sw1.WriteLine("<body background=" + QUOTE + m_cfg.BackgroundFile + QUOTE + " link=" + QUOTE + "#0000FF" + QUOTE + " vlink=" + QUOTE + "#800080" + QUOTE + " bgproperties=" + QUOTE + "fixed" + QUOTE + ">");
			//}
			//else
			//{
			//   sw1.WriteLine("<body link=" + QUOTE + "#0000FF" + QUOTE + " vlink=" + QUOTE + "#800080" + QUOTE + " bgproperties=" + QUOTE + "fixed" + QUOTE + ">");
			//}

			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<a name=" + QUOTE + "TOP" + QUOTE + "></a>");
			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.OccupationsPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			if (m_cfg.OccupationsPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.OccupationsPageText.Text + "</div>");

			sPrevOcc = "#";
			bool isFirst = true;
			bool didWriteOccs = false;
			for (iOccCount = 0; iOccCount <= iNumOcc - 1; iOccCount++)
			{
				if (_didCancel) goto BUILDOCC_EXIT;
				//strip off GUID
				sOccupation = sf.Left((string)sl.GetKey(iOccCount), sf.Len((string)sl.GetKey(iOccCount)) - 36);
				if (sOccupation != "")
				{
					sCurrOcc = sOccupation;
					if (sPrevOcc == "#") sPrevOcc = sCurrOcc;
					if (sPrevOcc != sCurrOcc)
					{
						sw1.WriteLine("</ul>");  //end prev list;
						sw1.Write("<h2>");
						sw1.Write(sCurrOcc);
						sw1.WriteLine("</h2>");
						sPrevOcc = sCurrOcc;
						sw1.WriteLine("<ul>");  //start a new list;
					}
					if (isFirst == true)
					{
						sw1.Write("<h2>");
						sw1.Write(sCurrOcc);
						sw1.WriteLine("</h2>");
						sPrevOcc = sCurrOcc;
						sw1.WriteLine("<ul>");  //start a new list;
						isFirst = false;
					}
					sw1.WriteLine("<li>" + MyOccupations[(int)sl.GetByIndex(iOccCount)].PersonName + "</li>");
					didWriteOccs = true;
				}
			} // iOccCount

			if (didWriteOccs)
				sw1.WriteLine("</ul>");  //end prev list;

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Index");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");

			sw1.WriteLine("<p style=\"text-align: center;\"><em>");

			if (m_cfg.HomeEqualsHomePage == false)
			{
				sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
			}

			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);

			//if (m_cfg.AddUserCopyright == true && m_cfg.AuthorsName != "")
		//{
		//   sw1.WriteLine("<p style=\"text-align: center; font-size:smaller; font-style: italic;\">");
		//   sw1.Write("Copyright &copy; " + DateTime.Now.Year.ToString() + "&nbsp;");
		//   if (m_cfg.AuthorsEmail != "")
		//   {
		//      sw1.Write("<a href=" + QUOTE + "mailto:" + m_cfg.AuthorsEmail + QUOTE + ">");
		//      sw1.Write(m_cfg.AuthorsName);
		//      if (m_cfg.AuthorsEmail != "") sw1.Write("</a>");
		//      sw1.WriteLine("</p>");
		//   }
		//}

			//sw1.WriteLine("<p style=\"text-align: center; font-size:smaller; font-style: italic;\">");
		//sw1.WriteLine("This page was created on " + DateTime.Now.ToString("dd MMMM yyyy") + " using <a href=" + QUOTE + "http://gatheringleaves.org/uged" + QUOTE + " target=_blank>UncleGed - GEDCOM-to-HTML software</a>  ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00")); //);
		//sw1.WriteLine("Copyright &copy; 1999-2007 D. M. DeBacker");
		//sw1.WriteLine("</p>");
		//sw1.WriteLine("</body>");
		//sw1.WriteLine("</html>");

		BUILDOCC_EXIT:
			sw1.Close();
			fs1.Close();
			sw2.Close();
			fs2.Close();
		}

		/// <summary>
		/// Summary of GetOccupations.
		/// </summary>
		/// 		
		private void GetOccupations()
		{
			//added in 3.03
			int iCnt;
			int jCnt;
			int iTotItems;
			string sEventDetails;
			string sOccupation;
			string sName;
			int iNumEvents;

			char Q = '"';

			//wipe everything out
			MyOccupations = new Occupations();

			//life events
			iTotItems = MyIndividuals.Count;

			if (iTotItems != 0)
			{
				for (iCnt = 0; iCnt <= iTotItems - 1; iCnt++)
				{
					iNumEvents = MyIndividuals[iCnt].LifeEvents.IncludedCount;
					if (iNumEvents != 0)
					{
						for (jCnt = 0; jCnt <= iNumEvents - 1; jCnt++)
						{
							sEventDetails = "";
							if (MyIndividuals[iCnt].LifeEvents[jCnt].EventType == LifeEvent.EventTypes.etOccupation)
							{
								if (m_cfg.OccLocEqualsOccText == true)
								{
									sOccupation = sf.Trim(MyIndividuals[iCnt].LifeEvents[jCnt].EventPlace);
								}
								else
								{
									sOccupation = sf.Trim(MyIndividuals[iCnt].LifeEvents[jCnt].EventDescription);
								}

								if (sf.Trim(sOccupation) == "") break;

								sOccupation = FixOccupation(sOccupation);

								sName = MyIndividuals[iCnt].Forenames + " " + MyIndividuals[iCnt].Surname;
								if (MyIndividuals[iCnt].Suffix != "")
								{
									sName = sName + ", " + MyIndividuals[iCnt].Suffix;
								}

								if (m_cfg.ChronoLinks == true) sEventDetails = "<a href=" + Q + MyIndividuals[iCnt].LinkPage + Q + ">";
								sEventDetails = sEventDetails + sName;
								if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "</a>";

								MyOccupations.Add(sOccupation, sEventDetails, CreateGUIDKey());
							}
						} // jCnt				
					} // iCnt
				}
			}
		}


		/// <summary>
		/// Summary of BuildFamilyPages.
		/// </summary>
		/// 		
		private bool BuildFamilyPages()
		{
			string sFile;
			int iFamCount;
			int iChildCount;
			int iNumFams;
			string FamRefID;
			string sTitle;
			string sHusband;
			string sWife;
			string sChTitle;
			string sChHusband;
			string sChWife;
			string sLink;
			ArrayList sLinks = new ArrayList();
			ArrayList sLnkFamID = new ArrayList();
			string sFather;
			string sMother;
			int iNumLinks;
			int iLinkcount;
			string sParents;
			bool bHasKids;
			int iNumEvents;
			int iNumSrcs;
			//			Surname NewSurname;
			string sSrnLink;
			bool bDoNotes;
			string sFamKey;
			bool bNotesOkay;
			string sListFile;
			string sChildName;
			bool bDidLine = false;
			string sGenUrl;
			DateTime dtLastChangeDate;
			//ver 4
			int NumOtherMarriages;
			ArrayList aryOtherFams;
			int iOtherCnt;

			FileStream fs1 = null;
			StreamWriter sw1 = null;
			FileStream fs2 = null;
			StreamWriter sw2 = null;

			string birthText = GetEventText(LifeEvent.EventTypes.etBirth, "", "BIRT");
			string deathText = GetEventText(LifeEvent.EventTypes.etDeath, "", "DEAT");

			try
			{
				iNumFams = MyFamilies.Count;

				char QUOTE = '"';

				if (m_cfg.CreateListReport == true)
				{
					sListFile = m_cfg.HtmlPath + "\\list.txt";

					fs2 = new FileStream(sListFile, FileMode.Create);
					sw2 = new StreamWriter(fs2);
				}

				if (iNumFams == 0) return true;
				for (iFamCount = 0; iFamCount <= iNumFams - 1; iFamCount++)
				{

					if (_didCancel) goto BUILDFAMILY_EXIT;
					bHasKids = false;
					//get the fam id
					FamRefID = MyFamilies[iFamCount].ID;

					dtLastChangeDate = MyFamilies[iFamCount].LastChangeDate; //start the last changed date;

					//establish the file name
					sFile = sf.Right(FamRefID, sf.Len(FamRefID) - 2);
					sFile = sf.Left(sFile, sf.Len(sFile) - 1);
					//BUG: UG blows up here when processing GENPRO.ged and LE_GENEALOGISTE.GED
					int iFileNumber = System.Convert.ToInt32(sFile); //added on v.6;
					sFile = iFileNumber.ToString("000");
					if (m_cfg.FirstFamily == 0)
					{
						m_cfg.FirstFamily = System.Convert.ToInt32(sFile);
						if (System.Convert.ToInt32(sFile) < m_cfg.FirstFamily)
						{
							m_cfg.FirstFamily = System.Convert.ToInt32(sFile);
						}
					}
					sSrnLink = m_cfg.FamFilePrefix + sFile + _pext;
					sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + sFile + _pext;
					FireStatusMessage("Building Family Page " + UncleGED.Utils.Tools.LongDirFix(sFile, 32) + "...");

					if (m_cfg.DoCompileHtml == true)
					{
						_HtmlHelp.AddFileToProject(sFile);
					}

					//the husband
					if (MyFamilies[iFamCount].Husband != "?")
					{
						sHusband = MyIndividuals[MyFamilies[iFamCount].Husband].Forenames + " " + MyIndividuals[MyFamilies[iFamCount].Husband].Surname;
						if (MyIndividuals[MyFamilies[iFamCount].Husband].Suffix != "")
						{
							sHusband = sHusband + ", " + MyIndividuals[MyFamilies[iFamCount].Husband].Suffix;
						}

						if (MyIndividuals[MyFamilies[iFamCount].Husband].Title != "")
						{
							sHusband = MyIndividuals[MyFamilies[iFamCount].Husband].Title + " " + sHusband;
						}

						if (MyIndividuals[MyFamilies[iFamCount].Husband].LastChangeDate > dtLastChangeDate) dtLastChangeDate = MyIndividuals[MyFamilies[iFamCount].Husband].LastChangeDate;

					}
					else
					{
						sHusband = "?";
					}

					//the wife
					if (MyFamilies[iFamCount].Wife != "?")
					{
						sWife = MyIndividuals[MyFamilies[iFamCount].Wife].Forenames + " " + MyIndividuals[MyFamilies[iFamCount].Wife].Surname;
						if (MyIndividuals[MyFamilies[iFamCount].Wife].Suffix != "")
						{
							sWife = sWife + ", " + MyIndividuals[MyFamilies[iFamCount].Wife].Suffix;
						}

						if (MyIndividuals[MyFamilies[iFamCount].Wife].Title != "")
						{
							sWife = MyIndividuals[MyFamilies[iFamCount].Wife].Title + " " + sWife;
						}

						if (MyIndividuals[MyFamilies[iFamCount].Wife].LastChangeDate > dtLastChangeDate) dtLastChangeDate = MyIndividuals[MyFamilies[iFamCount].Wife].LastChangeDate;
					}
					else
					{
						sWife = "?";
					}

					//title of web page
					sTitle = sHusband + "\\" + sWife;

					if (m_cfg.DisplayMRIN == true)
					{
						sTitle = sTitle + " (MRIN:" + FixRin(MyFamilies[iFamCount].ID) + ")";
					}

					//open the file
					fs1 = new FileStream(sFile, FileMode.Create);
					sw1 = new StreamWriter(fs1);

					//file header
					WriteHtmlHeader(sw1, sTitle);
					if (m_cfg.UseBanner)
					{
						sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
					}

					//add menu and title to family page
					if (m_cfg.FamPageMenuAtTop == true)
					{
						sw1.Write(GetMenu());
					}

					//husband details
					sw1.Write("<div class=\"household\"><p>");
					if (sHusband != "?")
					{
						sw1.Write("<b>" + sHusband + "</b>");

						if (m_cfg.DisplayRIN == true)
						{
							sw1.Write("&nbsp;(RIN: " + FixRin(MyIndividuals[MyFamilies[iFamCount].Husband].ID) + ")");
						}

						if (m_cfg.CreateListReport == true)
						{
							sw2.WriteLine("{0},{1},{2}", sHusband, MyIndividuals[MyFamilies[iFamCount].Husband].ReferenceNumber, sSrnLink);
						}

						if (m_cfg.CreateGendexFile == true)
						{
							//"GENDEX.txt"
							sGenUrl = sSrnLink;
							MyGendices.Add(sGenUrl, sf.UCase(MyIndividuals[MyFamilies[iFamCount].Husband].Surname), MyIndividuals[MyFamilies[iFamCount].Husband].Forenames + " /" + sf.UCase(MyIndividuals[MyFamilies[iFamCount].Husband].Surname) + "/", (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText), (MyIndividuals[MyFamilies[iFamCount].Husband].BirthPlace), (MyIndividuals[MyFamilies[iFamCount].Husband].DeathDateText), (MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace), CreateGUIDKey());
						}


						//find his parents
						sFamKey = MyIndividuals[MyFamilies[iFamCount].Husband].FamilyID + MyIndividuals[MyFamilies[iFamCount].Husband].ID;

						{
							Child child;
							try
							{
								child = MyChildren[sFamKey];
							}
							catch
							{
								child = null;
							}
							if (child != null)
							{
								//he is in this family
								sLink = "";
								sParents = "";
								sw1.Write(", son of ");
								//BUG: UG blows-up here when processing GENEATIQUE_POUR_WINDOWS.ged
								sLink = MyFamilies[child.FamilyID].ID;
								sLink = sf.Right(sLink, sf.Len(sLink) - 2);
								int iLinkNumber = System.Convert.ToInt32(sf.Left(sLink, sf.Len(sLink) - 1));
								sLink = iLinkNumber.ToString("000");
								sLink = m_cfg.FamFilePrefix + sLink + _pext;
								if (sLink != "") sw1.WriteLine("<a href=" + QUOTE + sLink + QUOTE + ">");
								if (MyFamilies[child.FamilyID].Husband != "?")
								{
									sFather = MyIndividuals[MyFamilies[child.FamilyID].Husband].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Husband].Surname;
									sParents = sFather;
									if (MyFamilies[child.FamilyID].Wife != "?")
									{
										sParents = sFather + " and ";
									}
								}
								if (MyFamilies[child.FamilyID].Wife != "?")
								{
									sMother = MyIndividuals[MyFamilies[child.FamilyID].Wife].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Wife].Surname;
									sParents = sParents + sMother;
								}
								sw1.WriteLine(sParents);
								if (sLink != "") sw1.WriteLine("</a>,");
							}
							child = null;
						}

						if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText != "")
						{
							if (sf.IsDate(MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText))
							{
								sw1.Write(" was born  " + MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText); //sf.DateValue(MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText).ToString("dd MMMM yyyy"));
							}
							else
							{
								sw1.Write(" was born  " + MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText);
							}
						}
						if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthPlace != "")
						{
							sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Husband].BirthPlace)));
							sw1.Write(". ");
						}
						else
						{
							sw1.Write(". ");
						}

						//CHANGE: if there is no actual marriage "MARR" then do not print the following sentence
						if (MyFamilies[iFamCount].DidMarry == true)
						{
							if (sWife != "?")
							{
								sw1.WriteLine("He married " + sWife);

								//if both individuals are not marked as private then print marriage data
								if (MyIndividuals[MyFamilies[iFamCount].Husband].IsPrivate == false && MyIndividuals[MyFamilies[iFamCount].Wife].IsPrivate == false)
								{
									if (MyFamilies[iFamCount].MarriageDateText != "")
									{
										sw1.Write(MyFamilies[iFamCount].MarriageDateText);
									}
									if (MyFamilies[iFamCount].MarriagePlace != "")
									{
										sw1.Write(" in " + FixEstLoc((MyFamilies[iFamCount].MarriagePlace)));
									}

								}
								sw1.Write(". ");
							}
						}

						if (MyIndividuals[MyFamilies[iFamCount].Husband].DeathDateText != "" || MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace != "")
						{
							sw1.Write("He died " + MyIndividuals[MyFamilies[iFamCount].Husband].DeathDateText);
							if (MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace != "")
							{
								sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace)));
							}
							sw1.Write(". ");
						}
					}

					//wife details
					if (sWife != "?")
					{
						sw1.Write("<b>" + sWife + "</b>");

						if (m_cfg.DisplayRIN == true)
						{
							sw1.Write("&nbsp;(RIN: " + FixRin(MyIndividuals[MyFamilies[iFamCount].Wife].ID) + ")");
						}

						if (m_cfg.CreateListReport == true)
						{
							sw2.WriteLine("{0},{1},{2}", sWife, MyIndividuals[MyFamilies[iFamCount].Wife].ReferenceNumber, sSrnLink);
						}

						if (m_cfg.CreateGendexFile == true)
						{
							//"GENDEX.txt"
							sGenUrl = sSrnLink;
							MyGendices.Add(sGenUrl, sf.UCase(MyIndividuals[MyFamilies[iFamCount].Wife].Surname), MyIndividuals[MyFamilies[iFamCount].Wife].Forenames + " /" + sf.UCase(MyIndividuals[MyFamilies[iFamCount].Wife].Surname) + "/", MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText, MyIndividuals[MyFamilies[iFamCount].Wife].BirthPlace, MyIndividuals[MyFamilies[iFamCount].Wife].DeathDateText, MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace, CreateGUIDKey());
						}

						//find her parents
						sFamKey = MyIndividuals[MyFamilies[iFamCount].Wife].FamilyID + MyIndividuals[MyFamilies[iFamCount].Wife].ID;

						{
							Child child;
							try
							{
								child = MyChildren[sFamKey];
							}
							catch { child = null; }
							if (child != null)
							{
								//she is in this family
								sLink = "";
								sParents = "";
								sw1.Write(", daughter of ");
								sLink = MyFamilies[child.FamilyID].ID;
								sLink = sf.Right(sLink, sf.Len(sLink) - 2);
								int iLinkNumber = System.Convert.ToInt32(sf.Left(sLink, sf.Len(sLink) - 1));
								sLink = iLinkNumber.ToString("000");
								sLink = m_cfg.FamFilePrefix + sLink + _pext;
								if (sLink != "") sw1.WriteLine("<a href=" + QUOTE + sLink + QUOTE + ">");
								if (MyFamilies[child.FamilyID].Husband != "?")
								{
									sFather = MyIndividuals[MyFamilies[child.FamilyID].Husband].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Husband].Surname;
									sParents = sFather;
									if (MyFamilies[child.FamilyID].Wife != "?")
									{
										sParents = sParents + " and ";
									}
								}
								if (MyFamilies[child.FamilyID].Wife != "?")
								{
									sMother = MyIndividuals[MyFamilies[child.FamilyID].Wife].Forenames + " " + MyIndividuals[MyFamilies[child.FamilyID].Wife].Surname;
									sParents = sParents + sMother;
								}
								sw1.WriteLine(sParents);
								if (sLink != "") sw1.WriteLine("</a>,");
							}
							child = null;
						}

						if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText != "")
						{
							sw1.Write(" was born  " + MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText);
						}
						if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthPlace != "")
						{
							sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Wife].BirthPlace)));
						}
						sw1.Write(". ");
						if (MyIndividuals[MyFamilies[iFamCount].Wife].DeathDateText != "" || MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace != "")
						{
							sw1.Write("She died " + MyIndividuals[MyFamilies[iFamCount].Wife].DeathDateText);
							if (MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace != "")
							{
								sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace)));
							}
							sw1.WriteLine(". ");
						}
					}
					sw1.WriteLine("</p>");
					sw1.Write("</div>");

					bHasKids = MyFamilies[iFamCount].HasChildren;

					if (bHasKids)
					{
						iChildCount = 0;
						sw1.WriteLine("<br/>");
						sw1.Write("Children of ");
						if (sHusband != "?")
						{
							sw1.Write(sHusband);
							sw1.Write(" and ");
						}
						if (sWife != "?")
						{
							sw1.Write(sWife);
						}
						sw1.WriteLine(" are:");
						sw1.WriteLine("<br/>");

						//listing children
						sw1.Write("<div class=\"children\">");
						sw1.WriteLine("<table border=" + QUOTE + "0" + QUOTE + " cellpadding=" + QUOTE + "7" + QUOTE + " cellspacing=" + QUOTE + "0" + QUOTE + " width=" + QUOTE + "100%" + QUOTE + ">");
						foreach (Child child in MyChildren)
						{
							if (child.FamilyID == FamRefID)
							{
								sLinks = new ArrayList();
								sLnkFamID = new ArrayList();
								iNumLinks = 0;
								//find out if the child has a family page
								if (MyIndividuals[child.ChildID].Sex == "M" && MyIndividuals[child.ChildID].HasOwnFamily)
								{
									if (MyIndividuals[child.ChildID].FamList.Count > 0)
									{
										for (iLinkcount = 0; iLinkcount <= MyIndividuals[child.ChildID].FamList.Count - 1; iLinkcount++)
										{
											sLnkFamID.Add(MyIndividuals[child.ChildID].FamList[iLinkcount].FamId);
											sLinks.Add(MyIndividuals[child.ChildID].FamList[iLinkcount].FamId);
											sLinks[iNumLinks] = sf.Right(sLinks[iNumLinks].ToString(), sf.Len(sLinks[iNumLinks].ToString()) - 2);
											sLinks[iNumLinks] = sf.Left(sLinks[iNumLinks].ToString(), sf.Len(sLinks[iNumLinks].ToString()) - 1);
											sLinks[iNumLinks] = System.Convert.ToInt32(sLinks[iNumLinks]).ToString("000");
											sLinks[iNumLinks] = m_cfg.FamFilePrefix + sLinks[iNumLinks] + _pext;
											iNumLinks++;
										} // iLinkcount
									}
								}
								if (MyIndividuals[child.ChildID].Sex == "F" && MyIndividuals[child.ChildID].HasOwnFamily)
								{
									if (MyIndividuals[child.ChildID].FamList.Count > 0)
									{
										for (iLinkcount = 0; iLinkcount <= MyIndividuals[child.ChildID].FamList.Count - 1; iLinkcount++)
										{
											sLnkFamID.Add(MyIndividuals[child.ChildID].FamList[iLinkcount].FamId);
											sLinks.Add(MyIndividuals[child.ChildID].FamList[iLinkcount].FamId);
											sLinks[iNumLinks] = sf.Right(sLinks[iNumLinks].ToString(), sf.Len(sLinks[iNumLinks].ToString()) - 2);
											sLinks[iNumLinks] = sf.Left(sLinks[iNumLinks].ToString(), sf.Len(sLinks[iNumLinks].ToString()) - 1);
											sLinks[iNumLinks] = System.Convert.ToInt32(sLinks[iNumLinks]).ToString("000");
											sLinks[iNumLinks] = m_cfg.FamFilePrefix + sLinks[iNumLinks] + _pext;
											iNumLinks++;
										} // iLinkcount
									}
								}

								iChildCount = iChildCount + 1;
								sw1.WriteLine("<tr>");
								sw1.WriteLine("<td valign=" + QUOTE + "top" + QUOTE + " width=" + QUOTE + "66%" + QUOTE + ">");
								sw1.Write(sf.Trim(iChildCount.ToString()) + ". " + MyIndividuals[child.ChildID].Forenames + " " + MyIndividuals[child.ChildID].Surname);

								if (m_cfg.DisplayRIN == true)
								{
									sw1.Write("&nbsp;(RIN: " + FixRin(MyIndividuals[child.ChildID].ID) + ")");
								}
								if (m_cfg.CreateListReport == true)
								{
									sChildName = MyIndividuals[child.ChildID].Forenames + " " + MyIndividuals[child.ChildID].Surname;
									sw2.WriteLine("{0},{1},{2}", sChildName, MyIndividuals[child.ChildID].ReferenceNumber, sSrnLink);
								}

								if (MyIndividuals[child.ChildID].LastChangeDate > dtLastChangeDate) dtLastChangeDate = MyIndividuals[child.ChildID].LastChangeDate;
								//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
								if (m_cfg.CreateGendexFile == true)
								{
									//"GENDEX.txt"
									sGenUrl = sSrnLink;
									MyGendices.Add(sGenUrl, sf.UCase(MyIndividuals[child.ChildID].Surname), MyIndividuals[child.ChildID].Forenames + " /" + sf.UCase(MyIndividuals[child.ChildID].Surname) + "/", (MyIndividuals[child.ChildID].BirthDateText), (MyIndividuals[child.ChildID].BirthPlace), (MyIndividuals[child.ChildID].DeathDateText), (MyIndividuals[child.ChildID].DeathPlace), CreateGUIDKey());
								}
								//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


								if (MyIndividuals[child.ChildID].BirthDateText != "")
								{
									sw1.WriteLine(", b. " + MyIndividuals[child.ChildID].BirthDateText);
								}
								sw1.WriteLine("</td>");
								sw1.WriteLine("<td valign=" + QUOTE + "top" + QUOTE + " width=" + QUOTE + "33%" + QUOTE + ">");
								sw1.WriteLine("<font size=\"2\"><em>");

								if (iNumLinks > 0)
								{

									for (iLinkcount = 0; iLinkcount <= iNumLinks - 1; iLinkcount++)
									{
										if (iLinkcount == 0)
										{
											sw1.WriteLine("See ");
										}
										else
										{
											sw1.WriteLine(" OR ");
										}

										sw1.WriteLine("<a href=" + QUOTE + sLinks[iLinkcount].ToString() + QUOTE + ">");


										//the husband
										if (MyFamilies[sLnkFamID[iLinkcount].ToString()].Husband != "?")
										{
											sChHusband = MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Husband].Forenames + " " + MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Husband].Surname;
											if (MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Husband].Suffix != "")
											{
												sChHusband += ", " + MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Husband].Suffix;
											}
										}
										else
										{
											sChHusband = "?";
										}

										//the wife
										if (MyFamilies[sLnkFamID[iLinkcount].ToString()].Wife != "?")
										{
											sChWife = MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Wife].Forenames + " " + MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Wife].Surname;
											if (MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Wife].Suffix != "")
											{
												sChWife += ", " + MyIndividuals[MyFamilies[sLnkFamID[iLinkcount].ToString()].Wife].Suffix;
											}
										}
										else
										{
											sChWife = "?";
										}

										//title of web page that we are linking to
										sChTitle = sChHusband + " &amp; " + sChWife;

										sw1.WriteLine(sChTitle);
										sw1.WriteLine("</a>");
									} //
								}
								sw1.WriteLine("</em></font>");
								sw1.WriteLine("</td>");
								sw1.WriteLine("</tr>");
							}
						} // child
						sw1.WriteLine("</table>");
						sw1.Write("</div>");
					}


					//--------marriage notes
					iNumEvents = MyFamilies[iFamCount].LifeEvents.IncludedCount;
					if (iNumEvents > 0 || MyFamilies[iFamCount].Notes != "")
					{
						bNotesOkay = true;
						if (MyFamilies[iFamCount].Husband != "?")
						{
							if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText == Privacy)
							{
								bNotesOkay = false;
							}
						}
						if (MyFamilies[iFamCount].Wife != "?")
						{
							if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText == Privacy)
							{
								bNotesOkay = false;
							}
						}

						if (bNotesOkay == true)
						{

							sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
							bDidLine = true;
							if (iNumEvents > 0)
							{
								sw1.Write("<div class=\"event\">");
								sw1.WriteLine("<p><em>Marriage/Union Events for " + sHusband + "\\" + sWife + ":</em></p>");
								sw1.WriteLine("<ul>");
								foreach (LifeEvent ev in MyFamilies[iFamCount].LifeEvents)
								{
									if(ev.EventText.Trim() != "!")
									{
										sw1.WriteLine("<li>");
										sw1.WriteLine(ev.EventText + ": ");
										if (ev.EventDescription.Length != 0) sw1.WriteLine(ev.EventDescription + "&nbsp;");
										if (ev.EventDateString.Length != 0) sw1.WriteLine(ev.EventDateString + "&nbsp;");
										if (ev.EventPlace != ev.EventText && ev.EventPlace.Length != 0) sw1.WriteLine(ev.EventPlace + "&nbsp;");
										if (ev.EventNote.Length != 0) sw1.WriteLine(ev.EventNote);
										sw1.WriteLine("</li>");
									}
								} // TheEvent
								sw1.WriteLine("</ul>");
								sw1.Write("</div>");
							}

							if (MyFamilies[iFamCount].Notes != "" && m_cfg.ExcludeNotes == false)
							{
								//if ( MyIndividuals(MyFamilies[iFamCount].Husband).BirthDateText != Privacy ) {
								sw1.WriteLine("<p><em>Marriage Notes for " + sHusband + "\\" + sWife + ":</em></p>");
								sw1.Write("<div class=\"notes\">");
								foreach (Note note in MyNotes)
								{
									if (note.ID == MyFamilies[iFamCount].Notes)
									{
										sw1.WriteLine("<p>" + note.NoteText + "</p>");
									}
								} // note
								sw1.Write("</div>");
							}

						}
					}

					//--------------other marriages?
					//CHANGE: now says "Other Marriages/Unions" instead of "Other Marriages"
					NumOtherMarriages = 0;
					if (MyFamilies[iFamCount].Husband != "?")
					{
						aryOtherFams = GetOtherMarriages(MyFamilies[iFamCount].Husband, MyFamilies[iFamCount].ID);
						if (aryOtherFams.Count != 0)
						{
							NumOtherMarriages = aryOtherFams.Count - 1;
							if (bDidLine == false) sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
							sw1.Write("<div class=\"othermarriages\">");
							sw1.WriteLine("<p><em>Other Marriages/Unions for " + sHusband + ":</em><br/>");
							for (iOtherCnt = 0; iOtherCnt <= NumOtherMarriages; iOtherCnt++)
							{
								sw1.WriteLine(GetOtherLink(aryOtherFams[iOtherCnt].ToString(), iOtherCnt, false));
							} // iOtherCnt
							sw1.WriteLine("</p>");
							sw1.Write("</div>");
						}
					}
					NumOtherMarriages = 0;
					if (MyFamilies[iFamCount].Wife != "?")
					{
						aryOtherFams = GetOtherMarriages(MyFamilies[iFamCount].Wife, MyFamilies[iFamCount].ID);
						if (aryOtherFams.Count != 0)
						{
							NumOtherMarriages = aryOtherFams.Count - 1;
							if (bDidLine == false) sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
							sw1.Write("<div class=\"othermarriages\">");
							sw1.WriteLine("<p><em>Other Marriages/Unions for " + sWife + ":</em><br/>");
							for (iOtherCnt = 0; iOtherCnt <= NumOtherMarriages; iOtherCnt++)
							{
								sw1.WriteLine(GetOtherLink(aryOtherFams[iOtherCnt].ToString(), iOtherCnt, false));
							} // iOtherCnt
							sw1.WriteLine("</p>");
							sw1.Write("</div>");
						}
					}
					bDidLine = false;

					//now the notes
					if (sHusband != "?")
					{

						iNumEvents = MyIndividuals[MyFamilies[iFamCount].Husband].LifeEvents.IncludedCount;
						bDidLine = false;

						if (iNumEvents > 0 || MyIndividuals[MyFamilies[iFamCount].Husband].Notes != "")
						{
							if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText != Privacy)
							{
								sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
								bDidLine = true;
								sw1.Write("<div class=\"events\">");
								sw1.WriteLine("<p><em>Notes for " + sHusband + ":</em></p>");
							}
						}

						if (MyIndividuals[MyFamilies[iFamCount].Husband].AliasName != "")
						{
							if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText != Privacy)
							{
								sw1.WriteLine("Also known as: " + MyIndividuals[MyFamilies[iFamCount].Husband].AliasName);
							}
						}

						if (iNumEvents > 0)
						{
							if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText != Privacy)
							{
								sw1.WriteLine("<ul>");
								if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText != "" || MyIndividuals[MyFamilies[iFamCount].Husband].BirthPlace != "")
								{
									if (sf.IsDate(MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText))
									{

										sw1.Write("<li>" + birthText + ": " + MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText); //sf.DateValue(MyIndividuals[child.ChildID].BirthDateText).ToString("dd MMMM yyyy"));
									}
									else
									{
										sw1.Write("<li>" + birthText + ": " + MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText);
									}
									if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthPlace != "")
									{
										sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Husband].BirthPlace)));
										sw1.WriteLine(". </li>");
									}
									sw1.WriteLine(". </li>");
								}

								if (MyIndividuals[MyFamilies[iFamCount].Husband].DeathDateText != "" || MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace != "")
								{
									sw1.Write("<li>" + deathText + ": " + MyIndividuals[MyFamilies[iFamCount].Husband].DeathDateText);
									if (MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace != "")
									{
										sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Husband].DeathPlace)));
									}
									sw1.WriteLine(". </li>");
								}

								foreach (LifeEvent ev in MyIndividuals[MyFamilies[iFamCount].Husband].LifeEvents)
								{
									if (ev.EventType != LifeEvent.EventTypes.etBirth && ev.EventType != LifeEvent.EventTypes.etDeath)
									{
										if (ev.EventText.Trim() != "!")
										{

											sw1.WriteLine("<li>");
											sw1.WriteLine(ev.EventText + ": ");
											if (ev.EventDescription.Length != 0) sw1.WriteLine(ev.EventDescription + "&nbsp;");
											if (ev.EventDateString.Length != 0) sw1.WriteLine(ev.EventDateString + "&nbsp;");
											if (ev.EventPlace != ev.EventText && ev.EventPlace.Length != 0) sw1.WriteLine(ev.EventPlace + "&nbsp;");
											if (ev.EventNote.Length != 0) sw1.WriteLine(ev.EventNote);
											sw1.WriteLine("</li>");
										}
									}
								} // ev
								if (MyIndividuals[MyFamilies[iFamCount].Husband].Nationality != "")
								{
									sw1.WriteLine("<li>");
									sw1.WriteLine("Nationality:&nbsp;" + MyIndividuals[MyFamilies[iFamCount].Husband].Nationality);
									sw1.WriteLine("</li>");
								}
								sw1.WriteLine("</ul>");
								sw1.Write("</div>");
							}
						}

						if (MyIndividuals[MyFamilies[iFamCount].Husband].Notes != "" && m_cfg.ExcludeNotes == false)
						{
							if (MyIndividuals[MyFamilies[iFamCount].Husband].BirthDateText != Privacy)
							{
								sw1.Write("<div class=\"notes\">");
								foreach (Note note in MyNotes)
								{
									if (note.ID == MyIndividuals[MyFamilies[iFamCount].Husband].Notes)
									{
										sw1.WriteLine("<p>" + note.NoteText + "</p>");
									}
								} // note
								sw1.Write("</div>");
							}
						}

						//sources for husband
						iNumSrcs = MyIndividuals[MyFamilies[iFamCount].Husband].Sources.Count;

						if (iNumSrcs > 0)
						{
							if (bDidLine == false) sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
							sw1.Write("<div class=\"sources\">");
							sw1.WriteLine("<p><em>Sources for " + sHusband + ":</em></p>");
						}


						if (iNumSrcs > 0)
						{
							//sw1.WriteLine("<font size=\"2\">");
							sw1.WriteLine("<ol>");
							foreach (Source src in MyIndividuals[MyFamilies[iFamCount].Husband].Sources)
							{
								sw1.Write("<li>");
								if (m_cfg.FamFilePrefix == "fam")
								{
									sw1.Write("<em>" + "<a href=" + QUOTE + "sources" + _pext + "#" + src.ID + QUOTE + " target=" + QUOTE + _srcTarget + QUOTE + ">" + MySources[src.ID].Title + "</a>, " + "</em>" + src.Page);
								}
								else
								{
									sw1.Write("<em>" + "<a href=" + QUOTE + m_cfg.FamFilePrefix + "_sources" + _pext + "#" + src.ID + QUOTE + " target=" + QUOTE + _srcTarget + QUOTE + ">" + MySources[src.ID].Title + "</a>, " + "</em>" + src.Page);
								}
								if (src.Note != "")
								{
									sw1.Write(src.Note + "&nbsp;");
								}
								if (src.SourceText != "")
								{
									sw1.Write("<em>" + src.SourceText + "</em>");
								}
								sw1.WriteLine("</li>");
							} // src
							sw1.WriteLine("</ol>");
							//sw1.WriteLine("</font>");
							sw1.Write("</div>");
						}

					}

					//*************NOTES FOR WIFE
					if (sWife != "?")
					{

						iNumEvents = MyIndividuals[MyFamilies[iFamCount].Wife].LifeEvents.IncludedCount;
						bDidLine = false;

						if (iNumEvents > 0 || MyIndividuals[MyFamilies[iFamCount].Wife].Notes != "")
						{
							if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText != Privacy)
							{
								sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
								bDidLine = true;
								sw1.Write("<div class=\"events\">");
								sw1.WriteLine("<p><em>Notes for " + sWife + ":</em></p>");
							}
						}

						if (MyIndividuals[MyFamilies[iFamCount].Wife].AliasName != "")
						{
							if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText != Privacy)
							{
								sw1.WriteLine("Also known as: " + MyIndividuals[MyFamilies[iFamCount].Wife].AliasName);
							}
						}

						if (iNumEvents > 0)
						{
							if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText != Privacy)
							{


								sw1.WriteLine("<ul>");
								if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText != "" || MyIndividuals[MyFamilies[iFamCount].Wife].BirthPlace != "")
								{
									if (sf.IsDate(MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText))
									{

										sw1.Write("<li>" + birthText + ": " + MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText); //sf.DateValue(MyIndividuals[child.ChildID].BirthDateText).ToString("dd MMMM yyyy"));
									}
									else
									{
										sw1.Write("<li>" + birthText + ": " + MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText);
									}
									if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthPlace != "")
									{
										sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Wife].BirthPlace)));
										sw1.WriteLine(". </li>");
									}
									sw1.WriteLine(". </li>");
								}

								if (MyIndividuals[MyFamilies[iFamCount].Wife].DeathDateText != "" || MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace != "")
								{
									sw1.Write("<li>" + deathText + ": " + MyIndividuals[MyFamilies[iFamCount].Wife].DeathDateText);
									if (MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace != "")
									{
										sw1.Write(" in " + FixEstLoc((MyIndividuals[MyFamilies[iFamCount].Wife].DeathPlace)));
									}
									sw1.WriteLine(". </li>");
								}

								foreach (LifeEvent ev in MyIndividuals[MyFamilies[iFamCount].Wife].LifeEvents)
								{
									if (ev.EventType != LifeEvent.EventTypes.etBirth && ev.EventType != LifeEvent.EventTypes.etDeath)
									{
										if (ev.EventText.Trim() != "!")
										{
											sw1.WriteLine("<li>");
											sw1.WriteLine(ev.EventText + ": ");
											if (ev.EventDescription.Length != 0) sw1.WriteLine(ev.EventDescription + "&nbsp;");
											if (ev.EventDateString.Length != 0) sw1.WriteLine(ev.EventDateString + "&nbsp;");
											if (ev.EventPlace != ev.EventText && ev.EventPlace.Length != 0) sw1.WriteLine(ev.EventPlace + "&nbsp;");
											if (ev.EventNote.Length != 0) sw1.WriteLine(ev.EventNote);
											sw1.WriteLine("</li>");
										}
									}
								} // ev
								if (MyIndividuals[MyFamilies[iFamCount].Wife].Nationality != "")
								{
									sw1.WriteLine("<li>");
									sw1.WriteLine("Nationality:&nbsp;" + MyIndividuals[MyFamilies[iFamCount].Wife].Nationality);
									sw1.WriteLine("</li>");
								}
								sw1.WriteLine("</ul>");
								sw1.Write("</div>");
							}
						}

						if (MyIndividuals[MyFamilies[iFamCount].Wife].Notes != "" && m_cfg.ExcludeNotes == false)
						{
							if (MyIndividuals[MyFamilies[iFamCount].Wife].BirthDateText != Privacy)
							{
								sw1.Write("<div class=\"notes\">");
								foreach (Note note in MyNotes)
								{
									if (note.ID == MyIndividuals[MyFamilies[iFamCount].Wife].Notes)
									{
										sw1.WriteLine("<p>" + note.NoteText + "</p>");
									}
								} // note
								sw1.Write("</div>");
							}
						}

						//sources for wife
						iNumSrcs = MyIndividuals[MyFamilies[iFamCount].Wife].Sources.Count;

						if (iNumSrcs > 0)
						{
							if (bDidLine == false) sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
							sw1.Write("<div class=\"sources\">");
							sw1.WriteLine("<p><em>Sources for " + sWife + ":</em></p>");
						}


						if (iNumSrcs > 0)
						{

							sw1.WriteLine("<ol>");
							foreach (Source src in MyIndividuals[MyFamilies[iFamCount].Wife].Sources)
							{
								sw1.Write("<li>");
								if (m_cfg.FamFilePrefix == "fam")
								{
									sw1.Write("<em>" + "<a href=" + QUOTE + "sources" + _pext + "#" + src.ID + QUOTE + " target=" + QUOTE + _srcTarget + QUOTE + ">" + MySources[src.ID].Title + "</a>, " + "</em>" + src.Page);
								}
								else
								{
									sw1.Write("<em>" + "<a href=" + QUOTE + m_cfg.FamFilePrefix + "_sources" + _pext + "#" + src.ID + QUOTE + " target=" + QUOTE + _srcTarget + QUOTE + ">" + MySources[src.ID].Title + "</a>, " + "</em>" + src.Page);
								}
								if (src.Note != "")
								{
									sw1.Write(src.Note + "&nbsp;");
								}
								if (src.SourceText != "")
								{
									sw1.Write("<em>" + src.SourceText + "</em>");
								}
								sw1.WriteLine("</li>");
							} // src
							sw1.WriteLine("</ol>");
							sw1.Write("</div>");
						}
					}

					//------------print notes for children without family records
					foreach (Child child in MyChildren)
					{
						if (child.FamilyID == FamRefID)
						{

							if (MyIndividuals[child.ChildID].HasOwnFamily == false)
							{
								//print the child//s notes on this page (this change decreased proc time by 30%)
								iNumEvents = MyIndividuals[child.ChildID].LifeEvents.IncludedCount;

								bDoNotes = false;
								if (iNumEvents > 0) bDoNotes = true;
								if (MyIndividuals[child.ChildID].Notes != "") bDoNotes = true;
								if (MyIndividuals[child.ChildID].BirthDateText != "") bDoNotes = true;
								if (MyIndividuals[child.ChildID].BirthDateText == Privacy) bDoNotes = false;

								bDidLine = false;
								//if we can print notes then print the notes
								if (bDoNotes)
								{
									sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
									bDidLine = true;
									sw1.Write("<div class=\"events\">");
									sw1.WriteLine("<p><em>Notes for " + MyIndividuals[child.ChildID].Forenames + " " + MyIndividuals[child.ChildID].Surname + ":</em></p>");

									if (MyIndividuals[child.ChildID].AliasName != "")
									{
										if (MyIndividuals[child.ChildID].BirthDateText != Privacy)
										{
											sw1.WriteLine("Also known as: " + MyIndividuals[child.ChildID].AliasName);
										}
									}

									sw1.WriteLine("<ul>");
									if (MyIndividuals[child.ChildID].BirthDateText != "" || MyIndividuals[child.ChildID].BirthPlace != "")
									{
										if (sf.IsDate(MyIndividuals[child.ChildID].BirthDateText))
										{

											sw1.Write("<li>" + birthText + ": " + MyIndividuals[child.ChildID].BirthDateText); //sf.DateValue(MyIndividuals[child.ChildID].BirthDateText).ToString("dd MMMM yyyy"));
										}
										else
										{
											sw1.Write("<li>" + birthText + ": " + MyIndividuals[child.ChildID].BirthDateText);
										}
										if (MyIndividuals[child.ChildID].BirthPlace != "")
										{
											sw1.Write(" in " + FixEstLoc((MyIndividuals[child.ChildID].BirthPlace)));
											sw1.WriteLine(". </li>");
										}
										sw1.WriteLine(". </li>");
									}

									if (MyIndividuals[child.ChildID].DeathDateText != "" || MyIndividuals[child.ChildID].DeathPlace != "")
									{
										sw1.Write("<li>" + deathText + ": " + MyIndividuals[child.ChildID].DeathDateText);
										if (MyIndividuals[child.ChildID].DeathPlace != "")
										{
											sw1.WriteLine(" in " + FixEstLoc((MyIndividuals[child.ChildID].DeathPlace)));
										}
										sw1.WriteLine(". </li>");
									}

									if (iNumEvents > 0)
									{
										if (MyIndividuals[child.ChildID].BirthDateText != Privacy)
										{
											foreach (LifeEvent ev in MyIndividuals[child.ChildID].LifeEvents)
											{
												//allow birth and death are recorded as events
												//they are handled separately (see above)
												if (ev.EventType != LifeEvent.EventTypes.etBirth && ev.EventType != LifeEvent.EventTypes.etDeath)
												{
													if (ev.EventText.Trim() != "!")
													{
														sw1.WriteLine("<li>");
														sw1.WriteLine(ev.EventText + ": ");
														if (ev.EventDescription.Length != 0) sw1.WriteLine(ev.EventDescription + "&nbsp;");
														if (ev.EventDateString.Length != 0) sw1.WriteLine(ev.EventDateString + "&nbsp;");
														if (ev.EventPlace != ev.EventText && ev.EventPlace.Length != 0) sw1.WriteLine(ev.EventPlace + "&nbsp;");
														if (ev.EventNote.Length != 0) sw1.WriteLine(ev.EventNote);
														sw1.WriteLine("</li>");
													}
												}
											} // TheEvent

											if (MyIndividuals[child.ChildID].Nationality != "")
											{
												sw1.WriteLine("<li>");
												sw1.WriteLine("Nationality:&nbsp;" + MyIndividuals[child.ChildID].Nationality);
												sw1.WriteLine("</li>");
											}
										}
									}
									sw1.WriteLine("</ul>");
									sw1.Write("</div>");

									if (MyIndividuals[child.ChildID].Notes != "" && m_cfg.ExcludeNotes == false)
									{
										if (MyIndividuals[child.ChildID].BirthDateText != Privacy)
										{
											sw1.Write("<div class=\"notes\">");
											foreach (Note note in MyNotes)
											{
												if (note.ID == MyIndividuals[child.ChildID].Notes)
												{
													sw1.WriteLine("<p>" + note.NoteText + "</p>");
												}
											} // note
											sw1.Write("</div>");
										}
									}
								} //end of notes

								//**********how about sources for this child?
								//**********added ver 3.01
								iNumSrcs = MyIndividuals[child.ChildID].Sources.Count;

								if (iNumSrcs > 0)
								{
									if (bDidLine == false) sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
									sw1.Write("<div class=\"sources\">");
									sw1.WriteLine("<p><em>Sources for " + MyIndividuals[child.ChildID].Forenames + " " + MyIndividuals[child.ChildID].Surname + ":</em></p>");
								}


								if (iNumSrcs > 0)
								{

									sw1.WriteLine("<ol>");
									foreach (Source TheSource in MyIndividuals[child.ChildID].Sources)
									{
										sw1.Write("<li>");
										if (m_cfg.FamFilePrefix == "fam")
										{
											sw1.Write("<em>" + "<a href=" + QUOTE + "sources" + _pext + "#" + TheSource.ID + QUOTE + " target=" + QUOTE + _srcTarget + QUOTE + ">" + MySources[TheSource.ID].Title + "</a>, " + "</em>" + TheSource.Page);
										}
										else
										{
											sw1.Write("<em>" + "<a href=" + QUOTE + m_cfg.FamFilePrefix + "_sources" + _pext + "#" + TheSource.ID + QUOTE + " target=" + QUOTE + _srcTarget + QUOTE + ">" + MySources[TheSource.ID].Title + "</a>, " + "</em>" + TheSource.Page);
										}
										if (TheSource.Note != "")
										{
											sw1.Write(TheSource.Note + "&nbsp;");
										}
										if (TheSource.SourceText != "")
										{
											sw1.Write("<em>" + TheSource.SourceText + "</em>");
										}
										sw1.WriteLine("</li>");
									} // TheSource
									sw1.WriteLine("</ol>");
									sw1.Write("</div>");
								}
								//********************end of sources for child
							}
						}
					} // child

					if (dtLastChangeDate > System.DateTime.FromOADate(0) && m_cfg.MostRecentOnPage == true)
					{
						sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
						sw1.Write("<div class=\"update\">");
						sw1.WriteLine("<p><center><em>The most recent update of information contained on this page was on:</em>&nbsp;" + dtLastChangeDate.ToString("dd MMMM yyyy") + "</p>");
						sw1.Write("</div>");
					}

					if (m_cfg.FamPageMenuAtTop == false)
					{
						//menu at bottom of family page
						sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
						sw1.Write("<div class=\"menu\">");

						sw1.WriteLine("<p style=\"text-align: center;\"><em>");
						if (m_cfg.FamFilePrefix == "fam")
						{
							sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
						}
						else
						{
							sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
						}
						sw1.WriteLine("Index");
						sw1.WriteLine("</a>");
						sw1.WriteLine("</em><br/>");
						//sw1.WriteLine("<p style=\"text-align: center;\"><em>");
						if (m_cfg.FamFilePrefix == "fam")
						{
							sw1.WriteLine("<a href=" + QUOTE + "surnames" + _pext + QUOTE + ">");
						}
						else
						{
							sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_surnames" + _pext + QUOTE + ">");
						}
						sw1.WriteLine("Surnames");
						sw1.WriteLine("</a>");
						sw1.WriteLine("</em><br/>");
						//sw1.WriteLine("<p style=\"text-align: center;\"><em>");
						if (m_cfg.HomeEqualsHomePage == false)
						{
							sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
						}
						else
						{
							sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
						}
						sw1.WriteLine("Table Of Contents");
						sw1.WriteLine("</a>");

						sw1.WriteLine("</em></p>");
						sw1.Write("</div>");

						sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
						//end menu at bottom of family page

					}
					WriteHtmlFooter(sw1);

					sw1.Close();
					fs1.Close();
				} // iFamCount

				if (m_cfg.CreateListReport == true)
				{
					sw2.Close();
					fs2.Close();
				}

				return true;

			BUILDFAMILY_EXIT:
				sw1.Close();
				fs1.Close();
				if (m_cfg.CreateListReport == true)
				{
					sw2.Close();
					fs2.Close();
				}

				return true;

			}
			catch (Exception ex)
			{

				FireStatusMessage("The following error occured in BuildFamilyPages while processing the GEDCOM file:" + '\n' + '\r' + "Error: " + ex.ToString());
				//FireStatusMessage("Incomplete");

				try
				{
					sw1.Close();
					fs1.Close();
					if (m_cfg.CreateListReport == true)
					{
						sw2.Close();
						fs2.Close();
					}
					return false;
				}
				catch { return false; }
			}

		}

		/// <summary>
		/// Writes a DOCTYPE Declaration to begining of the html file
		/// </summary>
		/// <param name="sw1"></param>
		/// <param name="Title">Title of page</param>
		private void WriteHtmlHeader(StreamWriter sw1, string Title)
		{
			int iCnt;
			string sSurname;

			sw1.WriteLine("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
			sw1.WriteLine("<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">");
			sw1.WriteLine("<head>");
			sw1.WriteLine("<title>" + Title + "</title>");
			sw1.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>");
			sw1.WriteLine("<meta name=\"Generator\" content=\"UncleGed Version 9.01\"/>");

			//optionally add keywords to page
			//if the surname count is greater that five, add it
			if ((m_cfg.AddKeywords == true))
			{
				sw1.Write("<meta name=\"keywords\" content=\"genealogy,geneology,GENEALOGY,GENEOLOGY,ancestry,family history,heritage,family tree");
				for (iCnt = 0; iCnt <= MySurnames.Count - 1; iCnt++)
				{ //- 1
					sSurname = sf.Trim(MySurnames[iCnt].FamilyName);
					if (sSurname != "" && MySurnames[iCnt].NumItems > 5)
					{
						sw1.Write("," + sSurname);
					}
				} // iCnt
				sw1.WriteLine("\"/>");

				sw1.WriteLine("<meta http-equiv=\"keywords\" content=\"genealogy geneology GENEALOGY GENEOLOGY ancestry family history heritage family tree\"/>");
			}

			if (m_cfg.CSSOption == 1)
			{
				sw1.WriteLine("<link rel = \"stylesheet\" type = \"text/css\" href=\"uged.css\"/>");
			}
			else if (m_cfg.CSSOption == 2 && _cssFile.Length != 0)
			{
				sw1.WriteLine("<link rel = \"stylesheet\" type = \"text/css\" href=\"{0}\"/>", _cssFile);
			}

			if (m_cfg.CreateRssFeed == true)
			{
				string rsslink =
					String.Format("<link rel=\"alternate\" type=\"application/rss+xml\" href=\"{0}/rssfeed.xml\" title=\"{1} RSS Feed\" />", m_cfg.RssRootURL, m_cfg.MainPageTitle);

				sw1.WriteLine(rsslink);
			}

			sw1.WriteLine("</head>");

			//body
			if (m_cfg.UseBackground == true && m_cfg.CSSOption == 0)
			{
				sw1.WriteLine("<body style=\"background-image: url('" + m_cfg.BackgroundFile + "');\">");
			}
			else if (m_cfg.CSSOption == 1 || m_cfg.CSSOption == 2)
			{
				sw1.WriteLine("<body>");
			}
			else
			{
				sw1.WriteLine("<body link=\"Blue\" vlink=\"Purple\">");
			}

		}

		/// <summary>
		/// writes the footer for all html pages
		/// </summary>
		/// <param name="sw1"></param>
		private void WriteHtmlFooter(StreamWriter sw1)
		{
			//TODO: include link to rss in footer

			sw1.Write("<div class=\"copyright\">");
			if (m_cfg.AddUserCopyright == true && m_cfg.AuthorsName != "")
			{
				sw1.WriteLine("<p>");
				sw1.Write("Copyright &copy; " + DateTime.Now.Year.ToString() + "&nbsp;");
				if (m_cfg.AuthorsEmail != "")
				{
					sw1.Write("<a href=\"mailto:" + m_cfg.AuthorsEmail + "\">");
					sw1.Write(m_cfg.AuthorsName);
					if (m_cfg.AuthorsEmail != "") sw1.Write("</a>");
					sw1.WriteLine("</p>");
				}
			}

			sw1.WriteLine("<p>");
			sw1.WriteLine("This page was created on " + DateTime.Now.ToString("dd MMMM yyyy") + " using <a href=\"http://gatheringleaves.org/uged\" target=\"_blank\">UncleGed - GEDCOM-to-HTML software</a>  ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00")); //);
			sw1.WriteLine("Copyright &copy; 1999-2009 D. M. DeBacker");
			sw1.WriteLine("</p>");
			sw1.WriteLine("</div>");
			sw1.WriteLine("</body>");
			sw1.WriteLine("</html>");
		}


		/// <summary>
		/// This build the ind_idx file.
		/// </summary>
		private void BuildIndexEach()
		{

			//BUG: UG blows up when building index with test file HEREDIS.GED

			string sFile;
			int iIndCount;
			int iNumInds;
			ArrayList sLinks = new ArrayList();
			ArrayList surnameTags = new ArrayList(0);
			string surnameTag = "";
			string surname = "";
			string sLink;
			string sFullName;
			string sCurrChar;
			string sPrevChar;
			string sUpdate;
			DateTime DaysAgo90;
			FileStream fs1;
			StreamWriter sw1;
			int iTag;
			string NameOnly;

			iNumInds = MyIndividuals.Count;
			sLinks.Capacity = iNumInds + 1;
			surnameTags.Capacity = iNumInds + 1;

			for (iIndCount = 0; iIndCount <= iNumInds - 1; iIndCount++)
			{
				sLinks.Add("");
				surnameTags.Add("");
			}

			SortedList sl = new SortedList(iNumInds);

			char QUOTE = '"';

			DaysAgo90 = DateTime.Now.AddDays(-m_cfg.DaysRecent);

			if (iNumInds == 0) return;

			sl.Clear();

			iIndCount = 0;
			foreach (Individual individual in MyIndividuals)
			{
				surname = sf.UCase(individual.Surname);

				if (surname == "") surname = "_";

				surnameTags[iIndCount] = String.Format("<div id={0}{1}{0}></div>", QUOTE, surname);

				sFullName = sf.UCase(individual.Surname) + ", " + individual.Forenames;
				if (individual.Suffix != "")
				{
					sFullName = sFullName + ", " + individual.Suffix;
				}

				//CHANGE: Surname page links now point to first occurrance of the surname in the indi index
				sFullName = sFullName + "</a></td><td width=" + QUOTE + "30%" + QUOTE + ">";

				if (individual.BirthDateText != "" && individual.BirthDateText != Privacy)
				{
					sFullName = sFullName + individual.BirthDateText;
				}

				sFullName = sFullName + "</td><td width=" + QUOTE + "30%" + QUOTE + ">";

				if (individual.LastChangeDate.CompareTo(_newDate) != 0 && m_cfg.MostRecentOnIndex == true)
				{
					sUpdate = "";
					if (m_cfg.HiLiteMostRecent && individual.LastChangeDate > DaysAgo90)
					{
						sUpdate = "<b>";
					}
					sUpdate = sUpdate + individual.LastChangeDate.ToString("dd MMM yyyy");
					if (m_cfg.HiLiteMostRecent && individual.LastChangeDate > DaysAgo90)
					{
						sUpdate = sUpdate + "</b>";
					}
					sFullName = sFullName + sUpdate;
				}

				sFullName = sFullName + "</td>";

				//find out which family page they go own
				//==========4.00.4097 optimization
				if (m_cfg.PageStyle == 0)
				{
					if (individual.HasOwnFamily == true)
					{
						if (individual.FamList[0].FamId.Length != 0)
						{

							sLinks[iIndCount] = individual.FamList[0].FamId;
							sLinks[iIndCount] = sf.Right(sLinks[iIndCount].ToString(), sf.Len(sLinks[iIndCount].ToString()) - 2);
							int iLinkNumber = System.Convert.ToInt32(sf.Left(sLinks[iIndCount].ToString(), sf.Len(sLinks[iIndCount].ToString()) - 1));
							sLinks[iIndCount] = iLinkNumber.ToString("000");
							sLinks[iIndCount] = m_cfg.FamFilePrefix + sLinks[iIndCount].ToString() + _pext;
							if (sLinks[iIndCount].ToString() != "")
							{
								sl.Add(sFullName + CreateGUIDKey(), iIndCount);
								individual.LinkPage = sLinks[iIndCount].ToString();
							}
						}
					}
					else
					{
						if (individual.FamilyID.Length != 0)
						{
							sLinks[iIndCount] = individual.FamilyID;
							sLinks[iIndCount] = sf.Right(sLinks[iIndCount].ToString(), sf.Len(sLinks[iIndCount].ToString()) - 2);
							int iLinkNumber = System.Convert.ToInt32(sf.Left(sLinks[iIndCount].ToString(), sf.Len(sLinks[iIndCount].ToString()) - 1));
							sLinks[iIndCount] = iLinkNumber.ToString("000");
							sLinks[iIndCount] = m_cfg.FamFilePrefix + sLinks[iIndCount].ToString() + _pext;
							if (sLinks[iIndCount].ToString() != "")
							{
								sl.Add(sFullName + CreateGUIDKey(), iIndCount);
								individual.LinkPage = sLinks[iIndCount].ToString();
							}
						}
					}
				}
				else
				{
					sLinks[iIndCount] = individual.LinkPage;
					sl.Add(sFullName + CreateGUIDKey(), iIndCount);
				}
				iIndCount++;
			} // individual

			if (m_cfg.FamFilePrefix == "fam")
			{
				sFile = m_cfg.HtmlPath + "\\ind_idx" + _pext;
			}
			else
			{
				sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_ind_idx" + _pext;
			}

			if (m_cfg.DoCompileHtml == true)
			{
				_HtmlHelp.AddFileToProject(sFile);
				_HtmlHelp.AddFileToContents("Index of Individuals", sFile);
			}

			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			//file header
			WriteHtmlHeader(sw1, m_cfg.IndiIndexPageTitle);

			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.IndiIndexPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());

			if (m_cfg.IndiIndexPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.IndiIndexPageText.Text + "</div>");

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<a name=" + QUOTE + "TOP" + QUOTE + "></a>");
			sw1.WriteLine("<center>");
			sw1.WriteLine("<a href=" + QUOTE + "#A" + QUOTE + ">A</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#B" + QUOTE + ">B</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#C" + QUOTE + ">C</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#D" + QUOTE + ">D</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#E" + QUOTE + ">E</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#F" + QUOTE + ">F</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#G" + QUOTE + ">G</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#H" + QUOTE + ">H</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#I" + QUOTE + ">I</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#J" + QUOTE + ">J</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#K" + QUOTE + ">K</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#L" + QUOTE + ">L</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#M" + QUOTE + ">M</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#N" + QUOTE + ">N</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#O" + QUOTE + ">O</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#P" + QUOTE + ">P</a>&nbsp;");

			sw1.WriteLine("<a href=" + QUOTE + "#Q" + QUOTE + ">Q</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#R" + QUOTE + ">R</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#S" + QUOTE + ">S</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#T" + QUOTE + ">T</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#U" + QUOTE + ">U</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#V" + QUOTE + ">V</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#W" + QUOTE + ">W</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#X" + QUOTE + ">X</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#Y" + QUOTE + ">Y</a>&nbsp;");
			sw1.WriteLine("<a href=" + QUOTE + "#Z" + QUOTE + ">Z</a>&nbsp;");
			sw1.WriteLine("</center>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sPrevChar = "#";

			if (m_cfg.DoCompileHtml == true) _HtmlHelp.OpenIndexFile();

			for (iIndCount = 0; iIndCount <= iNumInds - 1; iIndCount++)
			{
				if (_didCancel) return;
				if (iIndCount < sl.Count)
				{
					sFullName = sf.Left((string)sl.GetKey(iIndCount), sf.Len((string)sl.GetKey(iIndCount)) - 36); //strip off GUID;
				}
				else
				{
					sFullName = "";
				}

				if (sFullName != "")
				{
					sLink = sLinks[(int)sl.GetByIndex(iIndCount)].ToString();
					surnameTag = surnameTags[(int)sl.GetByIndex(iIndCount)].ToString();

					sCurrChar = sf.UCase(sf.Left(sf.Trim(sFullName), 1));
					if (sPrevChar != sCurrChar)
					{
						if (System.Convert.ToChar(sCurrChar) >= 'A')
						{
							sw1.WriteLine("</table>");//end the previous table;
							sw1.WriteLine("<p style=\"text-align: center;\"><em>" + "<a href=" + QUOTE + "#TOP" + QUOTE + ">" + "Top</a></em></p>");
							sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
						}
						sw1.WriteLine("<h2><a name=" + QUOTE + sCurrChar + QUOTE + ">");
						sw1.WriteLine(sCurrChar);
						sw1.WriteLine("</a></h2><br/>");
						sPrevChar = sCurrChar;
						//start a new table
						sw1.WriteLine("<table border=" + QUOTE + "0" + QUOTE + " cellpadding=" + QUOTE + "1" + QUOTE + " cellspacing=" + QUOTE + "1" + QUOTE + " width=" + QUOTE + "100%" + QUOTE + " > ");

						if (m_cfg.MostRecentOnIndex == true)
						{
							sw1.WriteLine("<tr><th width=" + QUOTE + "40%" + QUOTE + "><em>Name</em></th><th width=" + QUOTE + "30%" + QUOTE + "><em>Date Of Birth</em></th><th width=" + QUOTE + "30%" + QUOTE + "><em>Last Updated</em></th></tr>");
						}
						else
						{
							sw1.WriteLine("<tr><th width=" + QUOTE + "40%" + QUOTE + "><em>Name</em></th><th width=" + QUOTE + "30%" + QUOTE + "><em>Date Of Birth</em></th></tr>");
						}
					}
					sw1.Write("<tr><td width=" + QUOTE + "40%" + QUOTE + ">" + surnameTag + "<a href=" + QUOTE + sLink + QUOTE + ">");

					sw1.Write(sFullName);

					sw1.WriteLine("</tr>");

					if (m_cfg.DoCompileHtml == true)
					{
						iTag = sFullName.IndexOf('<');
						NameOnly = sf.Trim(sf.Left(sFullName, iTag - 1));
						_HtmlHelp.AddFileToIndex(NameOnly, sLink);
					}

				}
			} // iIndCount

			if (m_cfg.DoCompileHtml == true) _HtmlHelp.CloseIndexFile();

			sw1.WriteLine("</table>");//end the last table;
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>" + "<a href=" + QUOTE + "#TOP" + QUOTE + ">" + "Top</a></em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "surnames" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_surnames" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Surnames");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.HomeEqualsHomePage == false)
			{
				sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
			}
			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);

			sw1.Close();
			fs1.Close();
		}


		/// <summary>
		/// Summary of BuildBiblio.
		/// </summary>
		/// 		
		private void BuildBiblio()
		{
			string sFile;
			int cnt;
			int iNumSrcs;
			FileStream fs1;
			StreamWriter sw1;

			//MainForm.pbFile.value = 0

			iNumSrcs = MySources.Count;

			char QUOTE = '"';

			if (iNumSrcs == 0) return;

			//lFn = FreeFile()

			if (m_cfg.FamFilePrefix == "fam")
			{
				sFile = m_cfg.HtmlPath + "\\sources" + _pext;
			}
			else
			{
				sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_sources" + _pext;
			}

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(sFile);
				_HtmlHelp.AddFileToContents("Bibliography", sFile);
			}

			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			//file header
			WriteHtmlHeader(sw1, m_cfg.SourcesPageTitle);
			//sw1.WriteLine("<HTML>");
			//sw1.WriteLine("<HEAD>");
			//sw1.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">");
			//if (m_cfg.UseCSS == true) sw1.WriteLine("<LINK REL = stylesheet TYPE = text/css href=uged.css>");
			//sw1.WriteLine("<TITLE>" + m_cfg.SourcesPageTitle + "</TITLE>");
			//sw1.WriteLine("<META NAME=" + QUOTE + " Generator" + QUOTE + " CONTENT=" + QUOTE + " UncleGed Version " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00") + QUOTE + ">");
			//sw1.WriteLine("</HEAD>");
			//if (m_cfg.UseBackground)
			//{
			//   sw1.WriteLine("<body background=" + QUOTE + m_cfg.BackgroundFile + QUOTE + " link=" + QUOTE + "#0000FF" + QUOTE + " vlink=" + QUOTE + "#800080" + QUOTE + " bgproperties=" + QUOTE + "fixed" + QUOTE + ">");
			//}
			//else
			//{
			//   sw1.WriteLine("<body link=" + QUOTE + "#0000FF" + QUOTE + " vlink=" + QUOTE + "#800080" + QUOTE + " bgproperties=" + QUOTE + "fixed" + QUOTE + ">");
			//}
			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<a name=" + QUOTE + "TOP" + QUOTE + ">&nbsp;</a>");
			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.SourcesPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<ol>");

			for (cnt = 0; cnt <= iNumSrcs - 1; cnt++)
			{
				if (_didCancel) goto BUILDBIBLIO_EXIT;
				if (MySources[cnt].Author != "")
				{
					sw1.Write("<li>" + MySources[cnt].Author + ",&nbsp;<em>");
				}
				else
				{
					sw1.Write("<li>");
				}

				sw1.Write("<a name=" + MySources[cnt].ID + ">" + MySources[cnt].Title + "</a>");

				if (MySources[cnt].Author != "")
				{
					sw1.Write("</em>");
				}

				if (MySources[cnt].PublicationInfo != "")
				{
					sw1.Write(",&nbsp;" + MySources[cnt].PublicationInfo);
				}
				if (MySources[cnt].Media != "")
				{
					sw1.Write(",&nbsp;[" + MySources[cnt].Media + "]");
				}
				if (MySources[cnt].Note != "")
				{
					sw1.Write("&nbsp;<font size=\"2\">(" + MySources[cnt].Note + ")</font>");
				}
				sw1.WriteLine("<br/></li>");
			} // iSrcCount

			sw1.WriteLine("</ol>");

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Index");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.HomeEqualsHomePage == false)
			{
				sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
			}
			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);
		//if (m_cfg.AddUserCopyright == true && m_cfg.AuthorsName != "")
		//{
		//   sw1.WriteLine("<p style=\"text-align: center; font-size:2;\"><em>");
		//   sw1.Write("Copyright &copy; " + DateTime.Now.ToString("yyyy") + "&nbsp;");
		//   if (m_cfg.AuthorsEmail != "") sw1.Write("<a href=" + QUOTE + "mailto:" + m_cfg.AuthorsEmail + QUOTE + ">");
		//   sw1.Write(m_cfg.AuthorsName);
		//   if (m_cfg.AuthorsEmail != "") sw1.Write("</a>");
		//   sw1.WriteLine("</p>");
		//}

			//sw1.WriteLine("<p style=\"text-align: center; font-size:2;\"><em>");
		//sw1.WriteLine("This page was created on " + DateTime.Now.ToString("dd MMMM yyyy") + " using <a href=" + QUOTE + "http://gatheringleaves.org/uged" + QUOTE + " target=_blank>UncleGed - GEDCOM-to-HTML software</a>  ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00"));
		//sw1.WriteLine("Copyright &copy; 1999-2007 D. M. DeBacker");
		//sw1.WriteLine("</p>");
		//sw1.WriteLine("</body>");
		//sw1.WriteLine("</html>");

		BUILDBIBLIO_EXIT: ;
			sw1.Close();
			fs1.Close();
		}


		/// <summary>
		/// Summary of BuildSurnamePage.
		/// </summary>
		/// 		
		private void BuildSurnamePage()
		{
			string sFile;
			int iSrnCount;
			int iNumSrns;
			string sLink;
			string sFamilyName;
			string sItemCnt;
			ArrayList sLinks = new ArrayList();
			ArrayList iSrnCnt = new ArrayList();
			FileStream fs1;
			StreamWriter sw1;
			Surname NewSurname;

			//moved here in 0.7 count all of the surnames
			for (int i = 0; i <= MyIndividuals.Count - 1; i++)
			{
				NewSurname = new Surname();
				NewSurname.FamilyName = sf.UCase(MyIndividuals[i].Surname);
				NewSurname.FirstLink = MyIndividuals[i].LinkPage;
				if (MySurnames.Add(NewSurname) == false)
				{
					//if it failed to add then increment it
					MySurnames[NewSurname.FamilyName].NumItems = MySurnames[NewSurname.FamilyName].NumItems + 1;
				}
				NewSurname = null;
			}

			iNumSrns = MySurnames.Count;

			char QUOTE = '"';

			if (iNumSrns == 0) return;

			SortedList sl = new SortedList(iNumSrns);

			//put in sorted list for quick and dirty sort
			sl.Clear();

			for (iSrnCount = 0; iSrnCount <= iNumSrns - 1; iSrnCount++)
			{
				sl.Add(MySurnames[iSrnCount].FamilyName + CreateGUIDKey(), iSrnCount);
				sLinks.Add(MySurnames[iSrnCount].FirstLink);
				iSrnCnt.Add(MySurnames[iSrnCount].NumItems);
			} // iSrnCount


			if (m_cfg.FamFilePrefix == "fam")
			{
				sFile = m_cfg.HtmlPath + "\\surnames" + _pext;
			}
			else
			{
				sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_surnames" + _pext;
			}

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(sFile);
				_HtmlHelp.AddFileToContents("Surnames", sFile);
			}

			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			//file header
			WriteHtmlHeader(sw1, m_cfg.SurnamesPageTitle);
			//sw1.WriteLine("<HTML>");
			//sw1.WriteLine("<HEAD>");
			//sw1.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">");
			//if (m_cfg.UseCSS == true) sw1.WriteLine("<LINK REL = stylesheet TYPE = text/css href=uged.css>");
			//sw1.WriteLine("<TITLE>" + m_cfg.SurnamesPageTitle + "</TITLE>");
			//sw1.WriteLine("<META NAME=" + QUOTE + "Generator" + QUOTE + " CONTENT=" + QUOTE + "UncleGed Version " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00") + QUOTE + ">");
			//sw1.WriteLine("</HEAD>");
			//if (m_cfg.UseBackground)
			//{
			//   sw1.WriteLine("<body background=" + QUOTE + m_cfg.BackgroundFile + QUOTE + " link=" + QUOTE + "#0000FF" + QUOTE + " vlink=" + QUOTE + "#800080" + QUOTE + " bgproperties=" + QUOTE + "fixed" + QUOTE + ">");
			//}
			//else
			//{
			//   sw1.WriteLine("<body link=" + QUOTE + "#0000FF" + QUOTE + " vlink=" + QUOTE + "#800080" + QUOTE + " bgproperties=" + QUOTE + "fixed" + QUOTE + ">");
			//}
			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<a name=" + QUOTE + "TOP" + QUOTE + "></a>");
			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.SurnamesPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());


			if (m_cfg.SurnamesPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.SurnamesPageText.Text + "</div>");

			//sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			//sw1.WriteLine("<font size=\"2\">");
			//sw1.Write("The database is arranged in ascending order, beginning with the primary individual and branching out towards ");
			//sw1.Write("the ancestors of the primary individual. The surname links follow this pattern such that selecting a surname will ");
			//sw1.Write("take you to the first appearance of that surname on the branch leading from the primary individual. This does not mean ");
			//sw1.Write("that this is the only appearance of this surname and it is suggested that you follow links within the database or make use ");
			//sw1.WriteLine("of the index for finding more details.");
			//sw1.WriteLine("</font>");
			//sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p>");

			for (iSrnCount = 0; iSrnCount <= iNumSrns - 1; iSrnCount++)
			{
				sFamilyName = sf.Left((string)sl.GetKey(iSrnCount), sf.Len((string)sl.GetKey(iSrnCount)) - 36); //strip off GUID;
				if (sf.Trim(sFamilyName) != "")
				{
					sItemCnt = iSrnCnt[(int)sl.GetByIndex(iSrnCount)].ToString();

					//changed in 8.09
					//sLink = sLinks[(int)sl.GetByIndex(iSrnCount)].ToString();
					//sw1.WriteLine("<a href=" + QUOTE + sLink + QUOTE + ">" + sFamilyName + "</a>");
					if (m_cfg.FamFilePrefix == "fam")
					{
						sLink = "ind_idx" + _pext + "#" + sFamilyName;
					}
					else
					{
						sLink = m_cfg.FamFilePrefix + "_ind_idx" + _pext + "#" + sFamilyName;
					}

					sw1.WriteLine("<a href=" + QUOTE + sLink + QUOTE + ">" + sFamilyName + "</a>");
					if (m_cfg.AddSurnameCount == true) sw1.WriteLine("(" + sItemCnt + ")");
					sw1.WriteLine(",&nbsp; ");
				}
			} // iSrnCount

			sw1.WriteLine("</p>");

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Index");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.HomeEqualsHomePage == false)
			{
				sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
			}
			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);
			//if (m_cfg.AddUserCopyright == true && m_cfg.AuthorsName != "")
			//{
			//   sw1.WriteLine("<p style=\"text-align: center; font-size:2;\"><em>");
			//   sw1.Write("Copyright &copy; " + DateTime.Now.ToString("yyyy") + "&nbsp;");
			//   if (m_cfg.AuthorsEmail != "") sw1.Write("<a href=" + QUOTE + "mailto:" + m_cfg.AuthorsEmail + QUOTE + ">");
			//   sw1.Write(m_cfg.AuthorsName);
			//   if (m_cfg.AuthorsEmail != "") sw1.Write("</a>");
			//   sw1.WriteLine("</p>");
			//}

			//sw1.WriteLine("<p style=\"text-align: center; font-size:2;\"><em>");
			//sw1.WriteLine("This page was created on " + DateTime.Now.ToString("dd MMMM yyyy") + " using <a href=" + QUOTE + "http://gatheringleaves.org/uged" + QUOTE + " target=_blank>UncleGed - GEDCOM-to-HTML software</a>  ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00"));
			//sw1.WriteLine("Copyright &copy; 1999-2007 D. M. DeBacker");
			//sw1.WriteLine("</p>");
			//sw1.WriteLine("</body>");
			//sw1.WriteLine("</html>");

			sw1.Close();
			fs1.Close();

		}


		/// <summary>
		/// Summary of BuildTableOfContents.
		/// </summary>
		/// 		
		private void BuildTableOfContents()
		{
			string sFile;
			//			string  QUOTE;
			string sFirstFile;
			//int iCnt;
			//string sSurname;
			FileStream fs1;
			StreamWriter sw1;

			char QUOTE = '"';

			sFile = m_cfg.HtmlPath + "\\index" + _pext;

			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			WriteHtmlHeader(sw1, m_cfg.MainPageTitle);

			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<a name=" + QUOTE + "TOP" + QUOTE + ">&nbsp;</a>");
			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.MainPageTitle + "</h1>");

			if (m_cfg.SuppressLastUpdateOnHomePage == false)
			{
				sw1.WriteLine("<p style=\"text-align:center;\">Last updated " + DateTime.Now.ToString("dd MMMM yyyy") + "</p>");
			}
			
			sw1.WriteLine("<hr/>");
			sw1.WriteLine("<h2>Table of Contents</h2>");
			sw1.WriteLine("<ul>");

			if (m_cfg.PageStyle == 0)
			{
				sFirstFile = m_cfg.FamFilePrefix + m_cfg.FirstFamily.ToString("000") + _pext;
				sw1.WriteLine("<li><a href=" + QUOTE + sFirstFile + QUOTE + ">Family Pages</a>");
			}

			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine(" <li><a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">Index of Names</a>");
			}
			else
			{
				sw1.WriteLine(" <li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">Index of Names</a>");
			}

			if (MySources.Count > 0)
			{
				if (m_cfg.FamFilePrefix == "fam")
				{
					sw1.WriteLine("<li><a href=" + QUOTE + "sources" + _pext + QUOTE + ">Sources (Bibliography)</a>");
				}
				else
				{
					sw1.WriteLine("<li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_sources" + _pext + QUOTE + ">Sources (Bibliography)</a>");
				}
			}
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<li><a href=" + QUOTE + "surnames" + _pext + QUOTE + ">Surnames</a>");
			}
			else
			{
				sw1.WriteLine("<li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_surnames" + _pext + QUOTE + ">Surnames</a>");
			}
			if (m_cfg.CreateStatsPage == true)
			{
				if (m_cfg.FamFilePrefix == "fam")
				{
					sw1.WriteLine("<li><a href=" + QUOTE + "stats" + _pext + QUOTE + ">Statistics</a>");
				}
				else
				{
					sw1.WriteLine("<li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_stats" + _pext + QUOTE + ">Statistics</a>");
				}
			}
			if (m_cfg.CreateChronoPage == true)
			{
				if (m_cfg.FamFilePrefix == "fam")
				{
					sw1.WriteLine("<li><a href=" + QUOTE + "chrono" + _pext + QUOTE + ">Chronology Of Events</a>");
				}
				else
				{
					sw1.WriteLine("<li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_chrono" + _pext + QUOTE + ">Chronology Of Events</a>");
				}
			}
			if (m_cfg.CreateLocPage == true)
			{
				if (m_cfg.FamFilePrefix == "fam")
				{
					sw1.WriteLine("<li><a href=" + QUOTE + "locations" + _pext + QUOTE + ">List Of Locations &amp; Place Names</a>");
				}
				else
				{
					sw1.WriteLine("<li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_locations" + _pext + QUOTE + ">List Of Locations &amp; Place Names</a>");
				}
			}
			if (m_cfg.CreateOccupationsPage == true)
			{
				if (m_cfg.FamFilePrefix == "fam")
				{
					sw1.WriteLine("<li><a href=" + QUOTE + "occupations" + _pext + QUOTE + ">Occupations</a>");
				}
				else
				{
					sw1.WriteLine("<li><a href=" + QUOTE + m_cfg.FamFilePrefix + "_occupations" + _pext + QUOTE + ">Occupations</a>");
				}
			}
			if (m_cfg.DotlCreateDescendantOutline == true)
			{
				foreach (DescendantOutline dotl in MyDescendantOutlines)
				{
					sw1.WriteLine("<li><a href=" + QUOTE + dotl.PageFile + QUOTE + ">" + dotl.PageTitle + "</a>");
				}
			}

			sw1.WriteLine("</li>");
			sw1.WriteLine("</ul>");

			if (m_cfg.CreateRssFeed == true)
			{
				string rss = String.Format("<p style=\"text-align:center;\"><a href=\"{0}/rssfeed.xml\">Subscribe to additions and updates RSS Feed</a></p>", m_cfg.RssRootURL);
				sw1.WriteLine(rss);
			}

			if (m_cfg.MainPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.MainPageText.Text + "</div>");

			sw1.WriteLine("<hr/>");
			if (m_cfg.AuthorsUrl != "")
			{
				sw1.WriteLine("<center><a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">Main Page</a></center>");
			}

			WriteHtmlFooter(sw1);

			sw1.WriteLine("</body>");
			sw1.WriteLine("</html>");

			sw1.Close();
			fs1.Close();

		}

		/// <summary>
		/// Summary of BuildStatisticsPage.
		/// </summary>
		/// 		
		private void BuildStatisticsPage()
		{
			string sFile;
			int iCnt;
			FileStream fs1;
			StreamWriter sw1;

			float sngSources;
			float sngNotes;
			int iMissBirthDate = 0;
			int iMissBirthPlace = 0;
			int iPartBirthdate = 0;
			int iMissDeathDate = 0;
			int iPartDeathDate = 0;
			int iMissDeathPlace = 0;
			int NumInds;
			float sngStat;
			MoreStats myMoreStats = new MoreStats();

			char QUOTE = '"';

			if (m_cfg.FamFilePrefix == "fam")
			{
				sFile = m_cfg.HtmlPath + "\\stats" + _pext;
			}
			else
			{
				sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_stats" + _pext;
			}

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(sFile);
				_HtmlHelp.AddFileToContents("Statistics", sFile);
			}

			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			WriteHtmlHeader(sw1, m_cfg.StatsPageTitle);
			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.StatsPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			if (m_cfg.StatsPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.StatsPageText.Text + "</div>");

			sw1.WriteLine("<div align=" + QUOTE + "left" + QUOTE + ">");
			sw1.WriteLine("<table BORDER=" + QUOTE + "0" + QUOTE + " CELLPADDING=" + QUOTE + "0" + QUOTE + " CELLSPACING=" + QUOTE + "0" + QUOTE + " WIDTH=" + QUOTE + "100%" + QUOTE + ">");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Source:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.Source + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Source Name:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.SourceName + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Source Version:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.Version + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Creation Date:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.CreationDate + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Creation Time:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.CreationTime + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">GEDCOM File Name:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.SourceFile + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Submitter Name:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.SubmitterName + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Submitter E-mail:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFileInfo.SubmitterEmail + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Number Of Individuals:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyIndividuals.Count.ToString() + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Number Of Families:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyFamilies.Count.ToString() + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Number Of Notes:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MyNotes.Count.ToString() + "</td>");
			sw1.WriteLine("</tr>");
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Number Of Sources:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + MySources.Count.ToString() + "</td>");
			sw1.WriteLine("</tr>");

			if (MyNotes.Count > 0)
			{
				sngNotes = (float)MyNotes.Count / MyIndividuals.Count;
				sw1.WriteLine("<tr>");
				sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Notes / Individuals:</td>");
				sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngNotes.ToString("#,##0.00;;;Nil") + "</td>");
				sw1.WriteLine("</tr>");
			}
			else
			{
				sw1.WriteLine("<tr>");
				sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Notes / Individuals:</td>");
				sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">0</td>");
				sw1.WriteLine("</tr>");
			}

			if (MySources.Count > 0)
			{
				sngSources = (float)MySources.Count / MyIndividuals.Count;
				sw1.WriteLine("<tr>");
				sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Sources / Individuals:</td>");
				sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngSources.ToString("#,##0.00;;;Nil") + "</td>");
				sw1.WriteLine("</tr>");
			}
			else
			{
				sw1.WriteLine("<tr>");
				sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + ">Sources / Individuals:</td>");
				sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">0</td>");
				sw1.WriteLine("</tr>");
			}

			NumInds = MyIndividuals.Count;

			if (NumInds > 0)
			{
				for (iCnt = 0; iCnt <= NumInds - 1; iCnt++)
				{
					if (sf.Trim(MyIndividuals[iCnt].BirthDateText) == "") iMissBirthDate = iMissBirthDate + 1;
					if (sf.IsDate(MyIndividuals[iCnt].BirthDateText) == false && sf.Trim(MyIndividuals[iCnt].BirthDateText) != "") iPartBirthdate = iPartBirthdate + 1;
					if (sf.Trim(MyIndividuals[iCnt].DeathDateText) == "") iMissDeathDate = iMissDeathDate + 1;
					if (sf.IsDate(MyIndividuals[iCnt].DeathDateText) == false && sf.Trim(MyIndividuals[iCnt].DeathDateText) != "") iPartDeathDate = iPartDeathDate + 1;
					if (sf.Trim(MyIndividuals[iCnt].BirthPlace) == "") iMissBirthPlace = iMissBirthPlace + 1;
					if (sf.Trim(MyIndividuals[iCnt].DeathPlace) == "") iMissDeathPlace = iMissDeathPlace + 1;
				} // iCnt
			}

			sngStat = (float)iMissBirthDate / MyIndividuals.Count;
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> % Missing BirthDates:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngStat.ToString("#,##0.00;;;Nil") + "</td>");
			sw1.WriteLine("</tr>");

			sngStat = (float)iPartBirthdate / MyIndividuals.Count;
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> % Partial BirthDates:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngStat.ToString("#,##0.00;;;Nil") + "</td>");
			sw1.WriteLine("</tr>");

			sngStat = (float)iMissBirthPlace / MyIndividuals.Count;
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> % Missing Birth Place:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngStat.ToString("#,##0.00;;;Nil") + "</td>");
			sw1.WriteLine("</tr>");

			sngStat = (float)iMissDeathDate / MyIndividuals.Count;
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> % Missing Death Dates:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngStat.ToString("#,##0.00;;;Nil") + "</td>");
			sw1.WriteLine("</tr>");

			sngStat = (float)iPartDeathDate / MyIndividuals.Count;
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> % Partial Death Dates::</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngStat.ToString("#,##0.00;;;Nil") + "</td>");
			sw1.WriteLine("</tr>");

			sngStat = (float)iMissDeathPlace / MyIndividuals.Count;
			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> % Missing Death Place:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + sngStat.ToString("#,##0.00;;;Nil") + "</td>");
			sw1.WriteLine("</tr>");

			myMoreStats = GetMoreStats();

			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> Born Before 1600:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + myMoreStats.CountBornBefore1600.ToString() + "</td>");
			sw1.WriteLine("</tr>");

			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> Born in 17th C.:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + myMoreStats.CountBorn17thCentury.ToString() + "</td>");
			sw1.WriteLine("</tr>");

			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> Born in 18th C.:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + myMoreStats.CountBorn18thCentury.ToString() + "</td>");
			sw1.WriteLine("</tr>");

			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> Born in 19th C.:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + myMoreStats.CountBorn19thCentury.ToString() + "</td>");
			sw1.WriteLine("</tr>");

			sw1.WriteLine("<tr>");
			sw1.WriteLine("<td width=" + QUOTE + "25%" + QUOTE + "> Born in 20th C.:</td>");
			sw1.WriteLine("<td width=" + QUOTE + "75%" + QUOTE + ">" + myMoreStats.CountBorn20thCentury.ToString() + "</td>");
			sw1.WriteLine("</tr>");


			sw1.WriteLine("</table>");
			sw1.WriteLine("</div>");

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Index");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.HomeEqualsHomePage == false)
			{
				sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
			}
			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);
			//if (m_cfg.AddUserCopyright == true && m_cfg.AuthorsName != "")
			//{
			//   sw1.WriteLine("<p style=\"text-align: center; font-size:2;\"><em>");
			//   sw1.Write("Copyright &copy; " + DateTime.Now.ToString("yyyy") + "&nbsp;");
			//   if (m_cfg.AuthorsEmail != "") sw1.Write("<a href=" + QUOTE + "mailto:" + m_cfg.AuthorsEmail + QUOTE + ">");
			//   sw1.Write(m_cfg.AuthorsName);
			//   if (m_cfg.AuthorsEmail != "") sw1.Write("</a>");
			//   sw1.WriteLine("</p>");
			//}

			//sw1.WriteLine("<p style=\"text-align: center; font-size:2;\"><em>");
			//sw1.WriteLine("This page was created on " + DateTime.Now.ToString("dd MMMM yyyy") + " using <a href=" + QUOTE + "http://gatheringleaves.org/uged" + QUOTE + " target=_blank>UncleGed - GEDCOM-to-HTML software</a>  ver. " + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMajorPart + "." + System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileMinorPart.ToString("00"));
			//sw1.WriteLine("Copyright &copy; 1999-2007 D. M. DeBacker");
			//sw1.WriteLine("</p>");
			//sw1.WriteLine("</body>");
			//sw1.WriteLine("</html>");

			sw1.Close();
			fs1.Close();

		}


		/// <summary>
		/// Builds the chronology page
		/// </summary>
		/// 		
		private void BuildTimeLinePage()
		{
			string FilePath;
			int iYear;
			string sActualDate;
			string sIndexDate;
			DateTime dtDummyDate;
			FileStream fs1;
			StreamWriter sw1;

			int iCnt;
			int iNumItems;

			//create a collection of chronology entries
			GetTimeLineEvents();

			if (m_cfg.FamFilePrefix == "fam")
			{
				FilePath = m_cfg.HtmlPath + "\\chrono" + _pext;
			}
			else
			{
				FilePath = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_chrono" + _pext;
			}

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(FilePath);
				_HtmlHelp.AddFileToContents("Chronology Of Events", FilePath);
			}

			char QUOTE = '"';
			iYear = 0;

			iNumItems = MyTimeLineEntries.Count;
			SortedList sl = new SortedList(iNumItems);

			for (iCnt = 0; iCnt <= iNumItems - 1; iCnt++)
			{
				if (MyTimeLineEntries[iCnt].ActualDate == System.DateTime.FromOADate(0))
				{
					dtDummyDate = sf.DateValue("31 DEC " + MyTimeLineEntries[iCnt].TheYear.ToString());
					sIndexDate = dtDummyDate.ToString("yyyyMMdd");
				}
				else
				{
					sIndexDate = MyTimeLineEntries[iCnt].ActualDate.ToString("yyyyMMdd");
				}
				sl.Add(sIndexDate + CreateGUIDKey(), iCnt);
			} //

			fs1 = new FileStream(FilePath, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			WriteHtmlHeader(sw1, m_cfg.ChronoPageTitle);

			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.ChronoPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			if (m_cfg.ChronoPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.ChronoPageText.Text + "</div>");

			for (iCnt = 0; iCnt <= iNumItems - 1; iCnt++)
			{

				if (iYear != MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].TheYear)
				{
					if (iYear != 0)
					{ //end the previous list
						sw1.WriteLine("</ul>");
					}

					iYear = MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].TheYear;
					if (iYear < 1000)
					{
						sw1.WriteLine("<h2><font color=" + QUOTE + "black" + QUOTE + " face=" + QUOTE + "Book Antiqua" + QUOTE + ">AD " + iYear.ToString() + "</font></h2>");
					}
					else
					{
						sw1.WriteLine("<h2><font color=" + QUOTE + "black" + QUOTE + " face=" + QUOTE + "Book Antiqua" + QUOTE + ">" + iYear.ToString() + "</font></h2>");
					}
					sw1.WriteLine("<ul>");//start a new list;
				}

				sw1.WriteLine("<li style=" + QUOTE + "color: " + MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].ColorCode + QUOTE + ">");

				if (MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].ActualDate != System.DateTime.FromOADate(0))
				{
					sActualDate = MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].ActualDate.ToString("dd MMM");
					sw1.WriteLine("<b><font color=" + QUOTE + "black" + QUOTE + ">" + sActualDate + "</font></b>&nbsp;-&nbsp;");
				}

				if (MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].IsAbout == true)
				{
					switch (MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].EstimateStyle)
					{
						case 1: goto case 2;
						case 2: goto case 3;
						case 3: goto case 6;
						case 6:
							sw1.WriteLine("<font color=" + QUOTE + "black" + QUOTE + ">It was about this time that " + MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].EventDetails + "</font>");
							break;
						case 4:
							sw1.WriteLine("<font color=" + QUOTE + "black" + QUOTE + ">It was before this date that " + MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].EventDetails + "</font>");
							break;
						case 5:
							sw1.WriteLine("<font color=" + QUOTE + "black" + QUOTE + ">It was after this date that " + MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].EventDetails + "</font>");
							break;
					}
				}
				else
				{
					sw1.WriteLine("<font color=" + QUOTE + "black" + QUOTE + ">" + MyTimeLineEntries[(int)sl.GetByIndex(iCnt)].EventDetails + "</font>");
				}
				sw1.WriteLine("</li>");
			} // iCnt

			sw1.WriteLine("</ul>");

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Index");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);

			sw1.Close();
			fs1.Close();

		}


		#endregion

		#region Methods for building misc. files
		/// <summary>
		/// Summary of BuildGendexFile.
		/// </summary>
		/// 		
		private void BuildGendexFile()
		{
			string sFile;
			int iCnt;
			int iGendexEntrys;
			FileStream fs1;
			StreamWriter sw1;


			sFile = m_cfg.HtmlPath + "\\GENDEX.txt";
			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			iGendexEntrys = MyGendices.Count;

			if (iGendexEntrys > 0)
			{
				for (iCnt = 0; iCnt <= iGendexEntrys - 1; iCnt++)
				{

					if (sf.Trim(MyGendices[iCnt].Surname) != "")
					{
						sw1.Write(MyGendices[iCnt].PageUrl + "|");
						sw1.Write(MyGendices[iCnt].Surname + "|");
						sw1.Write(MyGendices[iCnt].Fullname + "|");
						if (sf.IsDate(MyGendices[iCnt].Birthdate))
						{
							sw1.Write(sf.DateValue(MyGendices[iCnt].Birthdate).ToString("dd MMM yyyy") + "|");
						}
						else
						{
							sw1.Write(MyGendices[iCnt].Birthdate + "|");
						}
						sw1.Write(MyGendices[iCnt].BirthPlace + "|");
						if (sf.IsDate(MyGendices[iCnt].DeathDate))
						{
							sw1.Write(sf.DateValue(MyGendices[iCnt].DeathDate).ToString("dd MMM yyyy") + "|");
						}
						else
						{
							sw1.Write(MyGendices[iCnt].DeathDate + "|");
						}
						sw1.WriteLine(MyGendices[iCnt].DeathPlace + "|");
					}
				}
			} // iCnt

			sw1.Close();
			fs1.Close();

		}


		/// <summary>
		/// Summary of BuildLocationsReport.
		/// </summary>
		/// 		
		private void BuildLocationsReport()
		{
			string sFile;
			//			string  sData;
			int iLocCount;
			int iNumLocs;
			//			string  QUOTE;
			string sCurrLoc = "";
			string sPrevLoc = "";
			string sLocation;
			string sFile2;

			FileStream fs1;
			StreamWriter sw1;

			FileStream fs2;
			StreamWriter sw2;

			GetLocations();

			iNumLocs = MyLocations.Count;

			SortedList sl = new SortedList(iNumLocs);

			char QUOTE = '"';

			if (iNumLocs == 0) return;


			for (iLocCount = 0; iLocCount <= iNumLocs - 1; iLocCount++)
			{
				sl.Add(MyLocations[iLocCount].LocationName + CreateGUIDKey(), iLocCount);
			} // iLocCount

			if (m_cfg.FamFilePrefix == "fam")
			{
				sFile = m_cfg.HtmlPath + "\\locations" + _pext;
			}
			else
			{
				sFile = m_cfg.HtmlPath + "\\" + m_cfg.FamFilePrefix + "_locations" + _pext;
			}

			if (m_cfg.DoCompileHtml)
			{
				_HtmlHelp.AddFileToProject(sFile);
				_HtmlHelp.AddFileToContents("Locations", sFile);
			}

			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			sFile2 = m_cfg.HtmlPath + "\\loc_report.txt";

			fs2 = new FileStream(sFile2, FileMode.Create);
			sw2 = new StreamWriter(fs2);

			//file header
			WriteHtmlHeader(sw1, m_cfg.LocationsPageTitle);

			if (m_cfg.UseBanner)
			{
				sw1.WriteLine("<p align=" + QUOTE + "center" + QUOTE + "><img src=" + QUOTE + m_cfg.BannerFile + QUOTE + "></p>");
			}

			sw1.WriteLine("<a name=" + QUOTE + "TOP" + QUOTE + "></a>");
			sw1.WriteLine("<h1 align=\"center\">" + m_cfg.LocationsPageTitle + "</h1>");
			sw1.WriteLine(GetMenu());
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			if (m_cfg.LocationsPageText.Text != "") sw1.WriteLine("<div class=\"extratext\">" + m_cfg.LocationsPageText.Text + "</div>");

			sPrevLoc = "#";
			bool isFirst = true;
			for (iLocCount = 0; iLocCount <= iNumLocs - 1; iLocCount++)
			{
				if (_didCancel) goto BUILDLOC_EXIT;
				sLocation = sf.Left((string)sl.GetKey(iLocCount), sf.Len((string)sl.GetKey(iLocCount)) - 36); //strip off GUID;
				if (sLocation != "")
				{
					sCurrLoc = sLocation;
					if (sPrevLoc == "#") sPrevLoc = sCurrLoc;
					if (sPrevLoc != sCurrLoc)
					{
						sw1.WriteLine("</ul>"); //end prev list;
						sw1.Write("<h2>");
						sw1.Write("<a name=LOC" + iLocCount.ToString("000") + ">" + sCurrLoc + "</a>");
						sw2.WriteLine(sCurrLoc);
						sw1.WriteLine("</h2>");
						sPrevLoc = sCurrLoc;
						sw1.WriteLine("<ul>");//start a new list;
					}

					if (isFirst == true)
					{
						sw1.Write("<h2>");
						sw1.Write("<a name=LOC" + iLocCount.ToString("000") + ">" + sCurrLoc + "</a>");
						sw2.WriteLine(sCurrLoc);
						sw1.WriteLine("</h2>");
						sPrevLoc = sCurrLoc;
						sw1.WriteLine("<ul>");//start a new list;
						isFirst = false;
					}

					sw1.WriteLine("<li>" + MyLocations[(int)sl.GetByIndex(iLocCount)].PersonName + " (" + GetEventText((MyLocations[(int)sl.GetByIndex(iLocCount)].EventType), "", "") + ")</li>");
				}
			} // iLocCount

			sw1.WriteLine("</ul>");//end prev list;

			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.FamFilePrefix == "fam")
			{
				sw1.WriteLine("<a href=" + QUOTE + "ind_idx" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.FamFilePrefix + "_ind_idx" + _pext + QUOTE + ">");
			}
			sw1.WriteLine("Index");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<p style=\"text-align: center;\"><em>");
			if (m_cfg.HomeEqualsHomePage == false)
			{
				sw1.WriteLine("<a href=" + QUOTE + "index" + _pext + QUOTE + ">");
			}
			else
			{
				sw1.WriteLine("<a href=" + QUOTE + m_cfg.AuthorsUrl + QUOTE + ">");
			}
			sw1.WriteLine("Table Of Contents");
			sw1.WriteLine("</a>");
			sw1.WriteLine("</em></p>");
			sw1.WriteLine("<hr size=\"1\" noshade=\"noshade\"/>");

			WriteHtmlFooter(sw1);

		BUILDLOC_EXIT:
			sw1.Close();
			fs1.Close();

			sw2.Close();
			fs2.Close();

		}


		/// <summary>
		/// Called by BuildLocationsReport to create as master list of all locations
		/// </summary>
		/// 		
		private void GetLocations()
		{
			//added in 3.01
			int iCnt;
			int jCnt;
			int iTotItems;
			string sEventDetails;
			string sLocation;
			string sHusband;
			string sWife;
			string sName;
			int iNumEvents;
			string sTemp;
			int iPageNum;
			string linkPage1 = "";
			string linkPage2 = "";
			char Q = '"';

			//wipe everything out
			MyLocations = new Locations();

			//marriages
			iTotItems = MyFamilies.Count;
			if (iTotItems != 0)
			{
				for (iCnt = 0; iCnt <= iTotItems - 1; iCnt++)
				{
					sEventDetails = "";
					//MainForm.pbFile.value = (iCnt / iTotItems) * 100
					sLocation = sf.Trim(MyFamilies[iCnt].MarriagePlace);

					if (sf.Trim(sLocation) == "") continue;

					if (sf.Left(sLocation, 1) == "[" || sf.Left(sLocation, 1) == "<")
					{
						switch (m_cfg.LocEstimateMethod)
						{
							case 0: //do not list;
								continue;
							case 1://treat same;
								sLocation = sf.Mid(sLocation, 2, sf.Len(sLocation) - 2);
								break;
							case 2://list at end;
								//nothing to do then
								break;
						}
					}

					sLocation = CleanLocation(sLocation);

					if (m_cfg.LocReverseOrder)
					{
						sLocation = ReverseLocation(sLocation);
					}

					if (MyFamilies[iCnt].Husband != "?")
					{
						linkPage1 = MyIndividuals[MyFamilies[iCnt].Husband].LinkPage;
						sHusband = MyIndividuals[MyFamilies[iCnt].Husband].Forenames + " " + MyIndividuals[MyFamilies[iCnt].Husband].Surname;
						if (MyIndividuals[MyFamilies[iCnt].Husband].Suffix != "")
						{
							sHusband = sHusband + ", " + MyIndividuals[MyFamilies[iCnt].Husband].Suffix;
						}
					}
					else
					{
						sHusband = "?";
					}

					if (MyFamilies[iCnt].Wife != "?")
					{
						linkPage2 = MyIndividuals[MyFamilies[iCnt].Wife].LinkPage;
						sWife = MyIndividuals[MyFamilies[iCnt].Wife].Forenames + " " + MyIndividuals[MyFamilies[iCnt].Wife].Surname;
						if (MyIndividuals[MyFamilies[iCnt].Wife].Suffix != "")
						{
							sWife = sWife + ", " + MyIndividuals[MyFamilies[iCnt].Wife].Suffix;
						}
					}
					else
					{
						sWife = "?";
					}


					if (m_cfg.PageStyle == 0) //family pages
					{
						//@F5@ - example family id - then numeric portion equals the page number
						sTemp = sf.Right(MyFamilies[iCnt].ID, sf.Len(MyFamilies[iCnt].ID) - 2);
						iPageNum = System.Convert.ToInt32(sf.Left(sTemp, sf.Len(sTemp) - 1));

						linkPage1 = m_cfg.FamFilePrefix + iPageNum.ToString("000") + _pext;
						linkPage2 = m_cfg.FamFilePrefix + iPageNum.ToString("000") + _pext;
					}

					if (sHusband != "?")
					{
						if (m_cfg.ChronoLinks == true) sEventDetails = "<a href=" + Q + linkPage1 + Q + ">";
						sEventDetails = sEventDetails + sHusband;
						if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "</a>";
					}
					else
					{
						sEventDetails = sHusband;
					}
					sEventDetails = sEventDetails + " and ";
					if (sWife != "?")
					{
						if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "<a href=" + Q + linkPage2 + Q + ">";
						sEventDetails = sEventDetails + sWife;
						if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "</a>";
					}
					else
					{
						sEventDetails = sEventDetails + sWife;
					}
					if (m_cfg.LocDisplayEventDates == true && MyFamilies[iCnt].MarriageDateText != "")
					{
						sEventDetails = sEventDetails + " - " + MyFamilies[iCnt].MarriageDateText;
					}
					if (MyFamilies[iCnt].MarriagePlace != "")
					{
						MyLocations.Add(sLocation, sEventDetails, LifeEvent.EventTypes.etMarriage, CreateGUIDKey());
					}
				} // iCnt
			}

			//births
			iTotItems = MyIndividuals.Count;
			if (iTotItems != 0)
			{
				for (iCnt = 0; iCnt <= iTotItems - 1; iCnt++)
				{
					sEventDetails = "";
					//MainForm.pbFile.value = (iCnt / iTotItems) * 100
					sLocation = sf.Trim(MyIndividuals[iCnt].BirthPlace);
					if (sf.Trim(sLocation) == "") continue;

					if (sf.Left(sLocation, 1) == "[" || sf.Left(sLocation, 1) == "<")
					{
						switch (m_cfg.LocEstimateMethod)
						{
							case 0: //do not list;
								continue;
							case 1: //treat same;
								sLocation = sf.Mid(sLocation, 2, sf.Len(sLocation) - 2);
								break;
							case 2: //list at end;
								//nothing to do then
								break;
						}
					}

					sLocation = CleanLocation(sLocation);

					if (m_cfg.LocReverseOrder)
					{
						sLocation = ReverseLocation(sLocation);
					}

					sName = MyIndividuals[iCnt].Forenames + " " + MyIndividuals[iCnt].Surname;
					if (MyIndividuals[iCnt].Suffix != "")
					{
						sName = sName + ", " + MyIndividuals[iCnt].Suffix;
					}
					if (m_cfg.ChronoLinks == true) sEventDetails = "<a href=" + Q + MyIndividuals[iCnt].LinkPage + Q + ">";
					sEventDetails = sEventDetails + sName;
					if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "</a>";
					if (m_cfg.LocDisplayEventDates == true && MyIndividuals[iCnt].BirthDateText != "")
					{
						sEventDetails = sEventDetails + " - " + MyIndividuals[iCnt].BirthDateText;
					}
					if (MyIndividuals[iCnt].BirthPlace != "")
					{
						MyLocations.Add(sLocation, sEventDetails, LifeEvent.EventTypes.etBirth, CreateGUIDKey());
					}
				} // iCnt
			}

			//deaths
			iTotItems = MyIndividuals.Count;
			if (iTotItems != 0)
			{
				for (iCnt = 0; iCnt <= iTotItems - 1; iCnt++)
				{
					sEventDetails = "";
					sLocation = sf.Trim(MyIndividuals[iCnt].DeathPlace);
					if (sf.Trim(sLocation) == "") continue;

					if (sf.Left(sLocation, 1) == "[" || sf.Left(sLocation, 1) == "<")
					{
						switch (m_cfg.LocEstimateMethod)
						{
							case 0: //do not list;
								continue;
							case 1: //treat same;
								sLocation = sf.Mid(sLocation, 2, sf.Len(sLocation) - 2);
								break;
							case 2: //list at end;
								//nothing to do then
								break;
						}
					}

					sLocation = CleanLocation(sLocation);

					if (m_cfg.LocReverseOrder)
					{
						sLocation = ReverseLocation(sLocation);
					}

					sName = MyIndividuals[iCnt].Forenames + " " + MyIndividuals[iCnt].Surname;
					if (MyIndividuals[iCnt].Suffix != "")
					{
						sName = sName + ", " + MyIndividuals[iCnt].Suffix;
					}

					if (m_cfg.ChronoLinks == true) sEventDetails = "<a href=" + Q + MyIndividuals[iCnt].LinkPage + Q + ">";
					sEventDetails = sEventDetails + sName;
					if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "</a>";
					if (m_cfg.LocDisplayEventDates == true && MyIndividuals[iCnt].DeathDateText != "")
					{
						sEventDetails = sEventDetails + " - " + MyIndividuals[iCnt].DeathDateText;
					}
					if (MyIndividuals[iCnt].DeathPlace != "")
					{
						MyLocations.Add(sLocation, sEventDetails, LifeEvent.EventTypes.etDeath, CreateGUIDKey());
					}
				} // iCnt
			}

			//life events
			iTotItems = MyIndividuals.Count;
			if (iTotItems != 0)
			{
				for (iCnt = 0; iCnt <= iTotItems - 1; iCnt++)
				{
					iNumEvents = MyIndividuals[iCnt].LifeEvents.IncludedCount;
					if (iNumEvents != 0)
					{
						for (jCnt = 0; jCnt <= iNumEvents - 1; jCnt++)
						{
							if (MyIndividuals[iCnt].LifeEvents[jCnt].EventType == LifeEvent.EventTypes.etBirth
								|| MyIndividuals[iCnt].LifeEvents[jCnt].EventType == LifeEvent.EventTypes.etDeath) continue;

							sEventDetails = "";

							if (MyIndividuals[iCnt].LifeEvents[jCnt].EventType == LifeEvent.EventTypes.etOccupation)
							{
								if (m_cfg.IgnoreOccupations == true) continue;
							}

							sLocation = sf.Trim(MyIndividuals[iCnt].LifeEvents[jCnt].EventPlace);
							if (sf.Trim(sLocation) == "") continue;

							if (sf.Left(sLocation, 1) == "[" || sf.Left(sLocation, 1) == "<")
							{
								switch (m_cfg.LocEstimateMethod)
								{
									case 0: //do not list;
										continue;
									case 1: //treat same;
										sLocation = sf.Mid(sLocation, 2, sf.Len(sLocation) - 2);
										break;
									case 2: //list at end;
										//nothing to do then
										break;
								}
							}

							sLocation = CleanLocation(sLocation);

							if (m_cfg.LocReverseOrder)
							{
								sLocation = ReverseLocation(sLocation);
							}

							sName = MyIndividuals[iCnt].Forenames + " " + MyIndividuals[iCnt].Surname;
							if (MyIndividuals[iCnt].Suffix != "")
							{
								sName = sName + ", " + MyIndividuals[iCnt].Suffix;
							}

							if (m_cfg.ChronoLinks == true) sEventDetails = "<a href=" + Q + MyIndividuals[iCnt].LinkPage + Q + ">";
							sEventDetails = sEventDetails + sName;
							if (m_cfg.ChronoLinks == true) sEventDetails = sEventDetails + "</a>";
							if (m_cfg.LocDisplayEventDates == true && MyIndividuals[iCnt].LifeEvents[jCnt].EventDateString != "")
							{
								sEventDetails = sEventDetails + " - " + MyIndividuals[iCnt].LifeEvents[jCnt].EventDateString;
							}

							if (MyIndividuals[iCnt].LifeEvents[jCnt].EventPlace != "")
							{
								MyLocations.Add(sLocation, sEventDetails, MyIndividuals[iCnt].LifeEvents[jCnt].EventType, CreateGUIDKey());
							}
						} // jCnt
					}
				} // iCnt
			}

		}

		/// <summary>
		/// This cleans up the location removing leading and trailing spaces
		/// in the location parts.
		/// </summary>
		/// <param name="sLocation"></param>
		/// <returns></returns>
		private string CleanLocation(string sLocation)
		{
			string sTemp = "";
			bool bIsEstimate = false;
			string sThisLocation;
			string[] locParts = new string[] { };

			try
			{


				if (sf.Trim(sLocation) == "")
				{
					return "";
				}

				sThisLocation = sf.Trim(sLocation);

				if (sf.Left(sThisLocation, 1) == "[" || sf.Left(sThisLocation, 1) == "<")
				{
					sThisLocation = sf.Mid(sThisLocation, 2, sf.Len(sThisLocation) - 2);
					bIsEstimate = true;
				}

				locParts = sThisLocation.Split(new Char[] { ',' });

				for (int i = 0; i <= locParts.GetUpperBound(0); i++)
				{
					sTemp += locParts[i].Trim();
					if (i != locParts.GetUpperBound(0)) sTemp += ", ";
				}

				sTemp = sf.Trim(sTemp);
				if (bIsEstimate == true && sTemp != "")
				{
					sTemp = "[" + sTemp + "]";
				}

				sTemp = StripLocLeadingPunc(sTemp);
				sTemp = StripLocTrailingPunc(sTemp);

				return sTemp.Trim();
			}
			catch
			{
				return sLocation;
			}
		}
		#endregion

		#region helper functions
		//HELPER FUNCTIONS

		/// <summary>
		/// Returns a text description of a given event type.
		/// </summary>
		/// <param name="EventType">the type of event</param>
		/// <param name="data">event data</param>
		/// <param name="tag">event tag</param>
		private string GetEventText(LifeEvent.EventTypes EventType, string data, string tag)
		{
			string eventText = data; //default if not found

			if (m_cfg.UseTagToText == true)
			{
				foreach (KeyValuePair<string, string> kvp in _slTagToText)
				{
					if (kvp.Key == tag) return kvp.Value;

				}
			}

			switch (EventType)
			{
				case LifeEvent.EventTypes.etUnknown: eventText = "Unknown event"; break;
				case LifeEvent.EventTypes.etDeathOfSpouse: eventText = "Death Of Spouse"; break;
				case LifeEvent.EventTypes.etBirth: eventText = "Birth"; break;
				case LifeEvent.EventTypes.etDeath: eventText = "Death"; break;
				case LifeEvent.EventTypes.etBurial: eventText = "Burial"; break;
				case LifeEvent.EventTypes.etOccupation: eventText = "Occupation"; break;
				case LifeEvent.EventTypes.etMarriage: eventText = "Marriage"; break;
				case LifeEvent.EventTypes.etResidence: eventText = "Residence"; break;
				case LifeEvent.EventTypes.etDivorce: eventText = "Divorce"; break;
				case LifeEvent.EventTypes.etImmigration: eventText = "Immigration"; break;
				case LifeEvent.EventTypes.etMilitary: eventText = "Military"; break;
				case LifeEvent.EventTypes.etOther: eventText = tag; break; 
				case LifeEvent.EventTypes.etElected: eventText = "Elected"; break;
				case LifeEvent.EventTypes.etExcommunicated: eventText = "Excommunicated"; break;
				case LifeEvent.EventTypes.etMission: eventText = "Mission"; break;
				case LifeEvent.EventTypes.etBaptismLDS: eventText = "Baptism LDS"; break;
				case LifeEvent.EventTypes.etBaptism: eventText = "Baptism"; break;
				case LifeEvent.EventTypes.etBar_Mitzvah: eventText = "Bar Mitzvah"; break;
				case LifeEvent.EventTypes.etBat_Mitzvah: eventText = "Bat Mitzah"; break;
				case LifeEvent.EventTypes.etBlessing: eventText = "Blessing"; break;
				case LifeEvent.EventTypes.etCaste: eventText = "Caste"; break;
				case LifeEvent.EventTypes.etCensus: eventText = "Census"; break;
				case LifeEvent.EventTypes.etChristening: eventText = "Christening"; break;
				case LifeEvent.EventTypes.etConfirmation: eventText = "Confirmation"; break;
				case LifeEvent.EventTypes.etConfirmationLDS: eventText = "Confirmation LDS"; break;
				case LifeEvent.EventTypes.etCremation: eventText = "Cremation"; break;
				case LifeEvent.EventTypes.etEducation: eventText = "Education"; break;
				case LifeEvent.EventTypes.etEmigration: eventText = "Emigration"; break;
				case LifeEvent.EventTypes.etEndowmentLDS: eventText = "Endowment LDS"; break;
				case LifeEvent.EventTypes.etFirstCommunion: eventText = "First Communion"; break;
				case LifeEvent.EventTypes.etGraduation: eventText = "Graduation"; break;
				case LifeEvent.EventTypes.etNaturalization: eventText = "Naturalization"; break;
				case LifeEvent.EventTypes.etOrdination: eventText = "Ordination"; break;
				case LifeEvent.EventTypes.etProbate: eventText = "Probate"; break;
				case LifeEvent.EventTypes.etProperty: eventText = "Property"; break;
				case LifeEvent.EventTypes.etReligion: eventText = "Religion"; break;
				case LifeEvent.EventTypes.etRetirement: eventText = "Retirement"; break;
				case LifeEvent.EventTypes.etSealedToParentsLDS: eventText = "Sealed To Parents LDS"; break;
				case LifeEvent.EventTypes.etSSN: eventText = "SSN"; break;
				case LifeEvent.EventTypes.etWill: eventText = "Will"; break;

			}

			return eventText;
		}


		/// <summary>
		/// Fix date strings for display purposes
		/// </summary>
		/// <param name="sData"></param>
		public string FixDate(string sData)
		{
			//fixes dates with EST, ABT, etc.
			string sTemp;
			int x;
			string sTemp2 = "";
			bool bHasAnd = false;
			string sDate;

			sDate = StripChar(sData, ".", true);

			sTemp = sf.Trim(sDate);

			if (sf.UCase(sf.Left(sTemp, 3)) == "ABT")
			{
				sTemp = sf.Trim(sf.Right(sTemp, sf.Len(sTemp) - 3));
				if (sf.IsDate(sTemp)) sTemp = sf.DateValue(sTemp).ToString("dd MMMM yyyy");
				//sTemp = "abt. " + sTemp;
				sTemp = m_cfg.DateStrings.ABT + " " + sTemp;
			}

			if (sf.UCase(sf.Left(sTemp, 3)) == "CAL")
			{
				sTemp = sf.Trim(sf.Right(sTemp, sf.Len(sTemp) - 3));
				if (sf.IsDate(sTemp)) sTemp = sf.DateValue(sTemp).ToString("dd MMMM yyyy");
				//sTemp = sTemp + " (calc)";
				sTemp = sTemp + " " + m_cfg.DateStrings.CAL;
			}

			if (sf.UCase(sf.Left(sTemp, 3)) == "EST")
			{
				sTemp = sf.Trim(sf.Right(sTemp, sf.Len(sTemp) - 3));
				if (sf.IsDate(sTemp)) sTemp = sf.DateValue(sTemp).ToString("dd MMMM yyyy");
				sTemp = sTemp + " " + m_cfg.DateStrings.EST;
			}

			if (sf.UCase(sf.Left(sTemp, 3)) == "BEF")
			{
				sTemp = sf.Trim(sf.Right(sTemp, sf.Len(sTemp) - 3));
				if (sf.IsDate(sTemp)) sTemp = sf.DateValue(sTemp).ToString("dd MMMM yyyy");
				sTemp = m_cfg.DateStrings.BEF + " " + sTemp;
			}

			if (sf.UCase(sf.Left(sTemp, 3)) == "AFT")
			{
				sTemp = sf.Trim(sf.Right(sTemp, sf.Len(sTemp) - 3));
				if (sf.IsDate(sTemp)) sTemp = sf.DateValue(sTemp).ToString("dd MMMM yyyy");
				//sTemp = "aft. " + sTemp;
				sTemp = m_cfg.DateStrings.AFT + " " + sTemp;
			}

			//FIX: since changes to between-and some dates are printed incorrectly "bet. January March, 1866" (see FTW.GED I1347 for example)
			//BET AND
			if (sf.UCase(sf.Left(sTemp, 3)) == "BET")
			{
				//BET AND
				sDate = sDate.ToUpper();
				sTemp = sDate.ToUpper();
				sTemp = sTemp.Replace("BETWEEN", "BET");

				sDate = sDate.Replace("-", "AND");
				sTemp = sTemp.Replace("-", "AND");

				x = sf.InStr(sDate, "AND");
				if (x != 0)
				{
					bHasAnd = true;
				}
				else
				{
					bHasAnd = false;
				}

				if (x > 0)
				{
					sTemp = sf.Trim(sf.Mid(sTemp, 5, x - 5));
				}
				else
				{
					sTemp = sf.Trim(sf.Right(sTemp, sf.Len(sTemp) - 3));
				}

				if (sf.IsDate(sTemp))
				{
					if (sTemp.Length < 9)
					{
						sTemp = sf.DateValue(sTemp).ToString("MMMM, yyyy");
					}
					else
					{
						sTemp = sf.DateValue(sTemp).ToString("dd MMMM yyyy");
					}
				}
				else
				{
					if (sTemp.Length == 3)
					{
						//assume it is a month and format it accordingly
						sTemp = ExpandMonthName(sTemp);

					}
				}


				if (x > 0 && bHasAnd == false)
				{
					sTemp2 = sf.Trim(sf.Right(sDate, sf.Len(sDate) - x));
				}
				else if (x > 0 && bHasAnd == true)
				{
					sTemp2 = sf.Trim(sf.Right(sDate, sf.Len(sDate) - (x + 2)));
				}

				if (sf.IsDate(sTemp2))
				{
					if (sTemp2.Length < 9)
					{
						sTemp2 = sf.DateValue(sTemp2).ToString("MMMM, yyyy");
					}
					else
					{
						sTemp2 = sf.DateValue(sTemp2).ToString("dd MMMM yyyy");
					}
				}
				else
				{
					if (sTemp2.Length == 3)
					{
						//assume it is a month and format it accordingly
						sTemp2 = ExpandMonthName(sTemp2);

					}
				}

				//sTemp = "between " + sTemp;
				sTemp = m_cfg.DateStrings.BET + " " + sTemp;

				if (x > 0)
				{
					sTemp = sTemp + " " + m_cfg.DateStrings.AND + " " + sTemp2;
				}

			} //end of between

			return sTemp;
		}

		/// <summary>
		/// Expands month names
		/// </summary>
		private string ExpandMonthName(string month)
		{
			string retStr = month;
			DateTime tmpMonth = new DateTime();

			switch (month)
			{
				case "JAN":
					tmpMonth = new DateTime(1999, 1, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "FEB":
					tmpMonth = new DateTime(1999, 2, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "MAR":
					tmpMonth = new DateTime(1999, 3, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "APR":
					tmpMonth = new DateTime(1999, 4, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "MAY":
					tmpMonth = new DateTime(1999, 5, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "JUN":
					tmpMonth = new DateTime(1999, 6, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "JUL":
					tmpMonth = new DateTime(1999, 7, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "AUG":
					tmpMonth = new DateTime(1999, 8, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "SEP":
					tmpMonth = new DateTime(1999, 9, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "OCT":
					tmpMonth = new DateTime(1999, 10, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "NOV":
					tmpMonth = new DateTime(1999, 11, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
				case "DEC":
					tmpMonth = new DateTime(1999, 12, 1);
					retStr = tmpMonth.ToString("MMMM");
					break;
			}

			return retStr;
		}


		/// <summary>
		/// Summary of FixRin.
		/// </summary>
		private string FixRin(string sRin)
		{
			string sTemp;
			sTemp = sf.Trim(sRin);
			sTemp = sf.Right(sTemp, sf.Len(sTemp) - 2);
			sTemp = sf.Left(sTemp, sf.Len(sTemp) - 1);
			return sTemp;
		}

		/// <summary>
		/// if the husband or wife is an empty string change it to a question mark
		/// </summary>
		/// 		
		private void FixFamiliesEach()
		{
			int iNumFams;

			iNumFams = MyFamilies.Count;

			if (iNumFams == 0) return;
			foreach (Family family in MyFamilies)
			{
				if (_didCancel) return;
				if (sf.Trim(family.Husband) == "")
				{
					family.Husband = "?";
				}
				if (sf.Trim(family.Wife) == "")
				{
					family.Wife = "?";
				}
			} // family //iFamCount

		}


		/// <summary>
		/// Mark an individual's dates as private if born 
		/// after a certain and does not have a death date
		/// </summary>
		/// 		
		private void MarkAsPrivate()
		{
			int cutoffYear = DateTime.Now.Year - m_cfg.BirthCutOffYears;

			//FIXED:re-evaluated how privacy is handle

			//_logger.DebugFormat("CutoffYear:={0}", cutoffYear);

			string sBirthYear = "";
			int birthYear = 0;

			foreach (Individual person in MyIndividuals)
			{
				try
				{
					//_logger.DebugFormat("MarkAsPrivate ID:={0} BirthDateText:={1} DeathDateText:={2}", person.ID, person.BirthDateText, person.DeathDateText);

					if (person.BirthDateText.Length >= 4 && person.DeathDateText.Length == 0)
					{
						sBirthYear = person.BirthDateText.Substring(person.BirthDateText.Length - 4, 4);
						birthYear = System.Int32.Parse(sBirthYear);

						if (birthYear > cutoffYear)
						{
							//_logger.Debug("Marked as private");
							person.BirthDateText = Privacy;
							person.IsPrivate = true;
						}
					}

				}
				catch //(Exception ex)
				{
					//_logger.DebugFormat("Error: {0}", ex.Message);
				}
			}
		}


		/// <summary>
		/// Summary of SecondsToHMS.
		/// </summary>
		private string SecondsToHMS(int lSeconds)
		{
			string sHour;
			string sMinute;
			string sSecond;
			int iHour;
			int iMinute;
			int iSecond;
			int lRemain;

			if (lSeconds >= 3600)
			{
				iHour = (int)(lSeconds / 60 / 60);
				lRemain = lSeconds - ((int)(iHour) * 60 * 60);
			}
			else
			{
				iHour = 0;
				lRemain = lSeconds;
			}

			sHour = iHour.ToString("00");

			if (lSeconds >= 60)
			{
				iMinute = (int)(lRemain / 60);
				lRemain = lSeconds - (((int)(iHour) * 60 * 60) + ((int)(iMinute) * 60));
			}
			else
			{
				iMinute = 0;
				lRemain = lSeconds;
			}

			sMinute = iMinute.ToString("00");

			iSecond = lRemain;
			sSecond = iSecond.ToString("00");

			return (sHour + ":" + sMinute + ":" + sSecond);
		}


		/// <summary>
		/// Returns the soundex code for the surname
		/// </summary>
		/// <param name="Surname"></param>
		private string GetSoundexCode(string Surname)
		{

			//1 - B,P,F,V
			//2 - C,S,G,J,K,Q,X,Z
			//3 - D,T
			//4 - L
			//5 - M,N
			//6 - R
			//
			//The letters A,E,I,O,U,Y,H, and W are not coded.
			//Names with adjacent letters having the same equivalent number are coded as one letter with a single number.
			//Surname prefixes are generally not used in the soundex.

			string sSoundex;
			string sTemp;
			int iCnt;
			int iIndx;
			string sChar;
			string sPrevChar;

			if (Surname == "")
			{
				return "";
			}

			Surname = sf.Trim(sf.UCase(Surname));

			if (sf.Left(Surname, 2) == "KN")
			{ //special case for names that begin w/ KN
				sSoundex = "N";
				sTemp = sf.Right(Surname, sf.Len(Surname) - 2);
			}
			else
			{
				sSoundex = sf.Left(Surname, 1);
				sTemp = sf.Right(Surname, sf.Len(Surname) - 1);
			}

			iIndx = 0;

			for (iCnt = 1; iCnt <= sf.Len(sTemp); iCnt++)
			{

				sChar = sf.Mid(sTemp, iCnt, 1);

				//if ( sChar != sPrevChar ) {
				switch (sChar)
				{
					case "B":
					case "P":
					case "F":
					case "V":
						if (sf.Right(sSoundex, 1) != "1")
						{
							sSoundex = sSoundex + "1";
							iIndx = iIndx + 1;
						}
						break;
					case "C":
					case "S":
					case "G":
					case "J":
					case "K":
					case "Q":
					case "X":
					case "Z":
						if (sf.Right(sSoundex, 1) != "2")
						{
							sSoundex = sSoundex + "2";
							iIndx = iIndx + 1;
						}
						break;
					case "D":
					case "T":
						if (sf.Right(sSoundex, 1) != "3")
						{
							sSoundex = sSoundex + "3";
							iIndx = iIndx + 1;
						}
						break;
					case "L":
						if (sf.Right(sSoundex, 1) != "4")
						{
							sSoundex = sSoundex + "4";
							iIndx = iIndx + 1;
						}
						break;
					case "M":
					case "N":
						if (sf.Right(sSoundex, 1) != "5")
						{
							sSoundex = sSoundex + "5";
							iIndx = iIndx + 1;
						}
						break;
					case "R":
						if (sf.Right(sSoundex, 1) != "6")
						{
							sSoundex = sSoundex + "6";
							iIndx = iIndx + 1;
						}
						break;
				}
				//}

				sPrevChar = sChar;
				if (iIndx == 3) break; //fixed in 2.1 was set to 4
			} // iCnt

			//pad with zeros
			if (sf.Len(sSoundex) == 1) sSoundex = sSoundex + "000";
			if (sf.Len(sSoundex) == 2) sSoundex = sSoundex + "00";
			if (sf.Len(sSoundex) == 3) sSoundex = sSoundex + "0";

			return sSoundex;

		}

		/// <summary>
		/// Summary of FindIndividual.
		/// </summary>
		private void FindIndividual(int iIndex)
		{

			//			Family MyFamily;
			//			Child MyChild;
			string TheFamID;

			DateTime ActualDate;

			int iGedFileIndex;
			string sSoundex;
			string sSurname;
			string sGivenName;
			string sSex;
			string sBirthPlace;
			string sBirthDateText;
			object datBirthDate;
			string sDeathPlace;
			string sDeathDateText;
			object datDeathDate;
			string sFather;
			string sMother;
			string sSpouse;
			string sMarriagePlace;
			string sMarriageDateText;
			object datMarriageDate;


			iGedFileIndex = 0;
			sSoundex = "";
			sSurname = "";
			sGivenName = "";
			sSex = "";
			sBirthPlace = "";
			sBirthDateText = "";
			datBirthDate = "";
			sDeathPlace = "";
			sDeathDateText = "";
			datDeathDate = "";
			sFather = "";
			sMother = "";
			sSpouse = "";
			sMarriagePlace = "";
			sMarriageDateText = "";
			datMarriageDate = "";


			//sFileName = sFile
			iGedFileIndex = iIndex;
			sSurname = sf.UCase(MyIndividuals[iIndex].Surname);
			sSoundex = GetSoundexCode((MyIndividuals[iIndex].Surname));
			sGivenName = MyIndividuals[iIndex].Forenames;
			if (MyIndividuals[iIndex].Suffix != "")
			{
				sGivenName = sGivenName + "," + MyIndividuals[iIndex].Suffix;
			}
			//sGivenName = sGivenName;
			sSex = MyIndividuals[iIndex].Sex;

			//birth info
			if (MyIndividuals[iIndex].BirthDateText != "" && MyIndividuals[iIndex].BirthDateText != Privacy)
			{
				sBirthDateText = MyIndividuals[iIndex].BirthDateText;
				sBirthPlace = MyIndividuals[iIndex].BirthPlace;
				ActualDate = sf.DateValue(MyIndividuals[iIndex].BirthDateText);
				if (ActualDate.Year != 1)
				{
					datBirthDate = ActualDate.Year;
					ActualDate = new DateTime();
				}
			}
			else if (MyIndividuals[iIndex].BirthDateText == Privacy)
			{
				sBirthDateText = Privacy;
			}

			//death info
			if (MyIndividuals[iIndex].DeathDateText != "" && MyIndividuals[iIndex].DeathDateText != Privacy)
			{
				sDeathDateText = MyIndividuals[iIndex].DeathDateText;
				sDeathPlace = MyIndividuals[iIndex].DeathPlace;
				ActualDate = sf.DateValue(MyIndividuals[iIndex].DeathDateText);
				if (ActualDate.Year != 1)
				{
					datDeathDate = ActualDate.Year;
					ActualDate = new DateTime();
				}
			}
			else if (MyIndividuals[iIndex].DeathDateText == Privacy)
			{
				sDeathDateText = Privacy;
			}

			//get spouse and marriage information
			switch (MyIndividuals[iIndex].Sex)
			{
				case "M":
					foreach (Family family in MyFamilies)
					{
						if (MyIndividuals[iIndex].ID == family.Husband)
						{
							sSpouse = "";
							if (family.Wife != "?")
							{
								sSpouse = MyIndividuals[family.Wife].Forenames;
								sSpouse = sSpouse + " " + sf.UCase(MyIndividuals[family.Wife].Surname);
								if (MyIndividuals[family.Wife].Suffix != "")
								{
									sSpouse = sSpouse + ", " + MyIndividuals[family.Wife].Suffix;
								}
							}
							//sSpouse = sSpouse;

							if (family.MarriageDateText != Privacy)
							{
								sMarriagePlace = family.MarriagePlace;
								sMarriageDateText = family.MarriageDateText;
								ActualDate = sf.DateValue(family.MarriageDateText);
								if (ActualDate.Year != 1)
								{
									datMarriageDate = ActualDate.Year;
									ActualDate = new DateTime();
								}
							}
							else
							{
								sMarriageDateText = Privacy;
							}
							break;
						}
					} // family
					break;
				case "F":
					foreach (Family family in MyFamilies)
					{
						if (MyIndividuals[iIndex].ID == family.Wife)
						{
							sSpouse = "";
							if (family.Husband != "?")
							{
								sSpouse = MyIndividuals[family.Husband].Forenames;
								sSpouse = sSpouse + " " + sf.UCase(MyIndividuals[family.Husband].Surname);
								if (MyIndividuals[family.Husband].Suffix != "")
								{
									sSpouse = sSpouse + ", " + MyIndividuals[family.Husband].Suffix;
								}
							}
							//sSpouse = sSpouse;
							if (family.MarriageDateText != Privacy)
							{
								sMarriagePlace = family.MarriagePlace;
								sMarriageDateText = family.MarriageDateText;
								ActualDate = sf.DateValue(family.MarriageDateText);
								if (ActualDate.Year != 1)
								{
									datMarriageDate = ActualDate.Year;
									ActualDate = new DateTime();
								}
							}
							else
							{
								sMarriageDateText = Privacy;
							}
							break;
						}
					} // MyFamily
					break;
			}
			//================
			//get info on parents
			TheFamID = FindIndFamily((MyIndividuals[iIndex].ID));
			if (TheFamID != "")
			{
				//he is in this family
				if (MyFamilies[TheFamID].Husband != "?")
				{
					sFather = MyIndividuals[MyFamilies[TheFamID].Husband].Forenames + " " + MyIndividuals[MyFamilies[TheFamID].Husband].Surname;
					sFather = sf.Trim(sFather);
				}
				if (MyFamilies[TheFamID].Wife != "?")
				{
					sMother = MyIndividuals[MyFamilies[TheFamID].Wife].Forenames + " " + MyIndividuals[MyFamilies[TheFamID].Wife].Surname;
					sMother = sf.Trim(sMother);
				}
			}

		}


		/// <summary>
		/// use a binary search to find the child family id for this individual
		/// </summary>
		private string FindIndFamily(string sIndID)
		{
			int MinIndex;
			int MaxIndex;
			string sSearch;
			int iFound;

			MinIndex = 1;
			MaxIndex = MyChildren.Count;
			sSearch = sIndID;

			iFound = BSearchChildren(MinIndex, MaxIndex, sSearch);

			if (iFound > 0)
			{
				return MyChildren[iFound].FamilyID;
			}
			else
			{
				return "";
			}

		}


		/// <summary>
		/// Summary of BSearchChildren.
		/// </summary>
		private int BSearchChildren(int First, int Last, string Value)
		{
			//perform a recursive binary search for the child//s family id
			int MidList;

			//is the middle one the one we are looking for?
			MidList = (int)((First + Last) / 2);
			if (MyChildren[MidList].ChildID == Value)
			{
				return MidList;
			}

			//check the first
			if (MyChildren[First].ChildID == Value)
			{
				return First;
			}

			//and the last
			if (MyChildren[Last].ChildID == Value)
			{
				return Last;
			}

			//if there is only one left in the list then exit
			if (MidList == First || MidList == Last)
			{
				return -1;
			}

			//half the list according to the Value//s position
			if (System.Convert.ToInt32(Value) < System.Convert.ToInt32(MyChildren[MidList].ChildID))
			{
				Last = MidList;
			}
			else
			{
				First = MidList + 1;
			}

			return BSearchChildren(First, Last, Value);

		}


		/// <summary>
		/// Summary of SetChildrenFlag.
		/// </summary>
		/// 		
		private void SetChildrenFlag()
		{
			int iNumFams;
			int iFamCount;
			string FamRefID;

			iNumFams = MyFamilies.Count;
			for (iFamCount = 0; iFamCount <= iNumFams - 1; iFamCount++)
			{
				if (_didCancel) return;
				FamRefID = MyFamilies[iFamCount].ID;
				foreach (Child child in MyChildren)
				{
					if (_didCancel) return;
					if (child.FamilyID == FamRefID)
					{
						MyFamilies[iFamCount].HasChildren = true;
						break;
					}
				}	 // child
			} // iFamCount
			return;
		}


		/// <summary>
		/// Summary of SetOwnFamilyFlag.
		/// </summary>
		/// 		
		private void SetOwnFamilyFlag()
		{

			int iChildCnt = 0;
			int iNumChildren;

			iNumChildren = MyChildren.Count;


			foreach (Child child in MyChildren)
			{
				if (_didCancel) return;
				iChildCnt = iChildCnt + 1;

				foreach (Family family in MyFamilies)
				{
					if (_didCancel) return;
					if (MyIndividuals[child.ChildID].Sex == "M")
					{
						if (child.ChildID == family.Husband)
						{
							MyIndividuals[child.ChildID].HasOwnFamily = true;
							break;
						}
					}
					else
					{
						if (child.ChildID == family.Wife)
						{
							MyIndividuals[child.ChildID].HasOwnFamily = true;
							break;
						}
					}
				} // family
			} // child

			return;
		}


		/// <summary>
		/// Summary of GetFilePart.
		/// </summary>
		private string GetFilePart(string sFullPath, short iOption)
		{
			string sDriveLetter;
			string sDirPath;
			string sFileName;
			string sExtension;
			int iPathLength;
			int iOffset;
			int iThisLength;
			bool bFileNameFound = false;

			sDriveLetter = "";
			sDirPath = "";
			sFileName = "";
			sExtension = "";

			if (sf.Mid(sFullPath, 2, 1) == ":")
			{ // Find the drive letter.
				sDriveLetter = sf.Left(sFullPath, 2);
				sFullPath = sf.Mid(sFullPath, 3);
			}
			iPathLength = sf.Len(sFullPath);
			for (iOffset = iPathLength; iOffset <= 1; iOffset--)
			{ // Find the next delimiter.
				switch (sf.Mid(sFullPath, iOffset, 1))
				{
					case ".":// This indicates either an extension or a . or a ..;
						iThisLength = sf.Len(sFullPath) - iOffset;
						if (iThisLength >= 1 && iThisLength <= 3)
						{ // sExtension
							sExtension = sf.Mid(sFullPath, iOffset, iThisLength + 1);
						}
						sFullPath = sf.Left(sFullPath, iOffset - 1);
						break;
					case "\\": // This indicates a path delimiter.;
						iThisLength = sf.Len(sFullPath) - iOffset;
						if (iThisLength >= 1 && iThisLength <= 8)
						{ // Filename
							sFileName = sf.Mid(sFullPath, iOffset + 1, iThisLength);
							sFullPath = sf.Left(sFullPath, iOffset);
							bFileNameFound = true;
							break;
						}
						break;
					default:
						break;
				}
			} // iOffset

			if (bFileNameFound == false)
			{
				sFileName = sFullPath;
			}
			else
			{
				sDirPath = sFullPath;
			} // Print to form for display.

			switch (iOption)
			{
				case GET_DRIVE:
					return sDriveLetter;
				case GET_DIRECTORY:
					return sDirPath;
				case GET_FILENAME:
					return sFileName;
				case GET_EXTENSION:
					return sExtension;
				default:
					return "";
			}

		}


		/// <summary>
		/// Summary of FixEstLoc.
		/// </summary>
		private string FixEstLoc(string sLocation)
		{
			//strips off "< >" from an estimated location - see Legacy 3.0

			try
			{

				string sTemp;


				sTemp = sf.Trim(sLocation);

				if (sf.Left(sTemp, 1) == "<" && sf.Right(sTemp, 1) == ">")
				{
					sTemp = sf.Right(sTemp, sf.Len(sTemp) - 1);
					sTemp = sf.Left(sTemp, sf.Len(sTemp) - 1);
					sTemp = "&lt;" + sTemp + "&gt;";
				}

				return sTemp;
			}
			catch
			{

				return sLocation;
			}
		}

		/// <summary>
		/// This function creates a collection chronology events
		/// </summary>
		/// 		
		private void GetTimeLineEvents()
		{
			//added in 3.01
			int i;
			int j;
			DateTime actualDate = new DateTime();
			int year = 0;
			int totalItems;
			string dateText;
			string eventDetails;
			string husband;
			string wife;
			string name;
			int numEvents;
			string eventText;
			bool isAbout = false;
			int estimateStyle = 0;
			string colorCode;
			string linkPage1 = "";
			string linkPage2 = "";
			string temp;
			int pageNum;
			char Q = '"';

			MyTimeLineEntries = new TimeLineEntries();

			//marriages
			totalItems = MyFamilies.Count;
			if (totalItems != 0)
			{
				for (i = 0; i <= totalItems - 1; i++)
				{
					eventDetails = "";
					//MainForm.pbFile.value = (iCnt / iTotItems) * 100
					dateText = MyFamilies[i].MarriageDateText;
					Tools.GetDates(dateText, ref actualDate, ref year, ref isAbout, ref estimateStyle);

					if (year <= m_cfg.ChronoCutOff && year != 0)
					{
						//only if husband is known and not private
						if (MyFamilies[i].Husband != "?" && MyIndividuals[MyFamilies[i].Husband].BirthDateText != Privacy)
						{
							linkPage1 = MyIndividuals[MyFamilies[i].Husband].LinkPage;
							husband = MyIndividuals[MyFamilies[i].Husband].Forenames + " " + MyIndividuals[MyFamilies[i].Husband].Surname;
							if (MyIndividuals[MyFamilies[i].Husband].Suffix != "")
							{
								husband = husband + ", " + MyIndividuals[MyFamilies[i].Husband].Suffix;
							}
						}
						else
						{
							husband = "?";
						}

						//only if wife is known and not private
						if (MyFamilies[i].Wife != "?" && MyIndividuals[MyFamilies[i].Wife].BirthDateText != Privacy)
						{
							linkPage2 = MyIndividuals[MyFamilies[i].Wife].LinkPage;
							wife = MyIndividuals[MyFamilies[i].Wife].Forenames + " " + MyIndividuals[MyFamilies[i].Wife].Surname;
							if (MyIndividuals[MyFamilies[i].Wife].Suffix != "")
							{
								wife = wife + ", " + MyIndividuals[MyFamilies[i].Wife].Suffix;
							}
						}
						else
						{
							wife = "?";
						}

						if (m_cfg.PageStyle == 0) //family pages
						{
							//@F5@ - example family id - then numeric portion equals the page number
							temp = sf.Right(MyFamilies[i].ID, sf.Len(MyFamilies[i].ID) - 2);
							pageNum = System.Convert.ToInt32(sf.Left(temp, sf.Len(temp) - 1));
							linkPage1 = m_cfg.FamFilePrefix + pageNum.ToString("000") + _pext;
							linkPage2 = m_cfg.FamFilePrefix + pageNum.ToString("000") + _pext;
						}

						if (husband != "?")
						{
							if (m_cfg.ChronoLinks == true) eventDetails = "<a href=" + Q + linkPage1 + Q + ">";
							eventDetails = eventDetails + husband;
							if (m_cfg.ChronoLinks == true) eventDetails = eventDetails + "</a>";
						}
						else
						{
							eventDetails = husband;
						}
						eventDetails = eventDetails + " and ";
						if (wife != "?")
						{
							if (m_cfg.ChronoLinks == true) eventDetails = eventDetails + "<a href=" + Q + linkPage2 + Q + ">";
							eventDetails = eventDetails + wife;
							if (m_cfg.ChronoLinks == true) eventDetails = eventDetails + "</a>";
						}
						else
						{
							eventDetails = eventDetails + wife;
						}
						eventDetails = eventDetails + " are married";

						if (MyFamilies[i].MarriagePlace != "")
						{
							eventDetails = eventDetails + " in " + MyFamilies[i].MarriagePlace;
						}
						else
						{
							eventDetails = eventDetails + ".";
						}
						if (year != 0 && (husband != "?" || wife != "?"))
						{
							colorCode = "#FFCCFF;";
							if (MyFamilies[i].DidMarry == true) //CHANGE: not displaying non-marriages
							{
								MyTimeLineEntries.Add(year, actualDate, eventDetails, isAbout, colorCode, estimateStyle, "M" + MyFamilies[i].ID);
							}
						}
					}
				} // iCnt
			}

			//births
			totalItems = MyIndividuals.Count;
			if (totalItems != 0)
			{
				for (i = 0; i <= totalItems - 1; i++)
				{
					eventDetails = "";
					dateText = MyIndividuals[i].BirthDateText;
					Tools.GetDates(dateText, ref actualDate, ref year, ref isAbout, ref estimateStyle);

					if (year <= m_cfg.ChronoCutOff && year != 0)
					{
						name = MyIndividuals[i].Forenames + " " + MyIndividuals[i].Surname;
						if (MyIndividuals[i].Suffix != "")
						{
							name = name + ", " + MyIndividuals[i].Suffix;
						}
						if (m_cfg.ChronoLinks == true) eventDetails = "<a href=" + Q + MyIndividuals[i].LinkPage + Q + ">";
						eventDetails = eventDetails + name;
						if (m_cfg.ChronoLinks == true) eventDetails = eventDetails + "</a>";
						eventDetails = eventDetails + " is born";
						if (MyIndividuals[i].BirthPlace != "")
						{
							eventDetails = eventDetails + " in " + MyIndividuals[i].BirthPlace;
						}
						else
						{
							eventDetails = eventDetails + ".";
						}
						if (year != 0 && MyIndividuals[i].Surname != "")
						{
							colorCode = "#008000;";
							MyTimeLineEntries.Add(year, actualDate, eventDetails, isAbout, colorCode, estimateStyle, "B" + MyIndividuals[i].ID);
						}
					}
				}
			}

			//deaths
			totalItems = MyIndividuals.Count;
			if (totalItems != 0)
			{
				for (i = 0; i <= totalItems - 1; i++)
				{
					eventDetails = "";
					dateText = MyIndividuals[i].DeathDateText;
					Tools.GetDates(dateText, ref actualDate, ref year, ref isAbout, ref estimateStyle);

					if (year <= m_cfg.ChronoCutOff && year != 0)
					{
						name = MyIndividuals[i].Forenames + " " + MyIndividuals[i].Surname;
						if (MyIndividuals[i].Suffix != "")
						{
							name = name + ", " + MyIndividuals[i].Suffix;
						}

						if (m_cfg.ChronoLinks == true) eventDetails = "<a href=" + Q + MyIndividuals[i].LinkPage + Q + ">";
						eventDetails = eventDetails + name;
						if (m_cfg.ChronoLinks == true) eventDetails = eventDetails + "</a>";
						eventDetails = eventDetails + " dies";
						if (MyIndividuals[i].DeathPlace != "")
						{
							eventDetails = eventDetails + " in " + MyIndividuals[i].DeathPlace;
						}
						else
						{
							eventDetails = eventDetails + ".";
						}
						if (year != 0 && MyIndividuals[i].Surname != "")
						{
							colorCode = "#FF0000;";
							MyTimeLineEntries.Add(year, actualDate, eventDetails, isAbout, colorCode, estimateStyle, "D" + MyIndividuals[i].ID);
						}
					}
				}
			}

			//life events
			totalItems = MyIndividuals.Count;
			if (totalItems != 0)
			{
				for (i = 0; i <= totalItems - 1; i++)
				{
					numEvents = MyIndividuals[i].LifeEvents.IncludedCount;
					if (numEvents != 0)
					{
						for (j = 0; j <= numEvents - 1; j++)
						{
							//skip if it is a birth or death event because they have already been handled
							if (MyIndividuals[i].LifeEvents[j].EventType == LifeEvent.EventTypes.etBirth ||
								MyIndividuals[i].LifeEvents[j].EventType == LifeEvent.EventTypes.etDeath) continue;

							//skip if the indvidual is marked as private
							if (MyIndividuals[i].BirthDateText == Privacy) continue;

							eventDetails = "";
							dateText = MyIndividuals[i].LifeEvents[j].EventDateString;
							Tools.GetDates(dateText, ref actualDate, ref year, ref isAbout, ref estimateStyle);

							if (year <= m_cfg.ChronoCutOff && year != 0)
							{
								name = MyIndividuals[i].Forenames + " " + MyIndividuals[i].Surname;
								if (MyIndividuals[i].Suffix != "")
								{
									name = name + ", " + MyIndividuals[i].Suffix;
								}

								eventText = MyIndividuals[i].LifeEvents[j].EventText;
								if (sf.Left(eventText, 5) == "Note ")
								{
									eventText = sf.Right(eventText, sf.Len(eventText) - 5);
								}

								if (m_cfg.ChronoLinks == true) eventDetails = "<a href=" + Q + MyIndividuals[i].LinkPage + Q + ">";
								eventDetails = eventDetails + name;
								if (m_cfg.ChronoLinks == true) eventDetails = eventDetails + "</a>";

								eventDetails = eventDetails + "&nbsp;-&nbsp;" + eventText;

								if (MyIndividuals[i].LifeEvents[j].EventPlace != "")
								{
									eventDetails = eventDetails + " (" + MyIndividuals[i].LifeEvents[j].EventPlace + ")";
								}
								else
								{
									eventDetails = eventDetails + ".";
								}
								if (year != 0 && MyIndividuals[i].Surname != "")
								{
									switch (MyIndividuals[i].LifeEvents[j].EventType)
									{
										case LifeEvent.EventTypes.etBaptism:
											goto case LifeEvent.EventTypes.etChristening;
										case LifeEvent.EventTypes.etChristening:
											colorCode = "#FFD700;";
											break;
										case LifeEvent.EventTypes.etBurial:
											goto case LifeEvent.EventTypes.etCremation;
										case LifeEvent.EventTypes.etCremation:
											colorCode = "#8B008B;";
											break;
										default:
											colorCode = "#0000FF;";
											break;
									}
									MyTimeLineEntries.Add(year, actualDate, eventDetails, isAbout, colorCode, estimateStyle, MyIndividuals[i].LifeEvents[j].ID);
								}
							}
						} // jCnt
					}
				} // iCnt
			}

		}


		/// <summary>
		/// Summary of GetMarriageLink.
		/// </summary>
		private string GetMarriageLink(string sLink1, string sLink2)
		{
			int Mrin1;
			int Mrin2;
			string sTemp;
			int iWhereDot;

			sTemp = sf.Right(sLink1, sf.Len(sLink1) - 3);
			iWhereDot = sf.InStr(sTemp, ".");
			if (iWhereDot != 0)
			{
				sTemp = sf.Left(sTemp, iWhereDot);
				Mrin1 = System.Convert.ToInt32(sTemp);
			}
			else
			{
				Mrin1 = 9999;
			}

			sTemp = sf.Right(sLink2, sf.Len(sLink2) - 3);
			iWhereDot = sf.InStr(sTemp, ".");
			if (iWhereDot != 0)
			{
				sTemp = sf.Left(sTemp, iWhereDot);
				Mrin2 = System.Convert.ToInt32(sTemp);
			}
			else
			{
				Mrin2 = 9999;
			}

			if (Mrin1 < Mrin2)
			{
				return sLink1;
			}
			else
			{
				return sLink2;
			}

		}


		/// <summary>
		/// Reverses the order of location parts
		/// </summary>
		/// <param name="sLocation">Location to be reversed</param>
		public string ReverseLocation(string sLocation)
		{
			string sTemp = "";
			bool bIsEstimate = false;
			string sThisLocation;
			string[] locParts = new string[] { };

			//FIX: bug in this routine was fixed in v.9.02

			try
			{


				if (sf.Trim(sLocation) == "")
				{
					return "";
				}

				sThisLocation = sf.Trim(sLocation);

				if (sf.Left(sThisLocation, 1) == "[" || sf.Left(sThisLocation, 1) == "<")
				{
					sThisLocation = sf.Mid(sThisLocation, 2, sf.Len(sThisLocation) - 2);
					bIsEstimate = true;
				}

				locParts = sThisLocation.Split(new Char[] { ',' });

				for (int i = locParts.GetUpperBound(0); i >= 0; i--)
				{
					sTemp += locParts[i].Trim();
					if (i > 0) sTemp += ", ";
				}

				sTemp = sf.Trim(sTemp);
				if (bIsEstimate == true && sTemp != "")
				{
					sTemp = "[" + sTemp + "]";
				}

				sTemp = StripLocLeadingPunc(sTemp);
				sTemp = StripLocTrailingPunc(sTemp);

				return sTemp.Trim();
			}
			catch
			{
				return sLocation;
			}
		}


		/// <summary>
		/// Summary of StripChar.
		/// </summary>
		private string StripChar(string sString, string sChar, bool bReplaceWithSpace)
		{
			string tempStr1;
			string tempStr2;

			tempStr1 = sString;
			tempStr1 = sString.Replace(sChar, " ");

			if (!bReplaceWithSpace)
			{
				//remove the spaces
				tempStr2 = tempStr1.Replace(" ", "");
				tempStr1 = tempStr2;
			}

			return tempStr1;
		}


		/// <summary>
		/// Cleans up the occupation title
		/// </summary>
		private string FixOccupation(string sOccupation)
		{

			string sTemp;
			int iCnt;
			string sTest;

			sOccupation = sf.Trim(sOccupation);
			sTest = "OCCUPATION: ";
			if (sf.UCase(sf.Left(sOccupation, sf.Len(sTest))) == sTest)
			{
				sOccupation = sf.Right(sOccupation, sf.Len(sOccupation) - sf.Len(sTest));
			}

			sTemp = sf.Trim(sOccupation);

			//strip off any articles such as a, an, or the
			if (sf.UCase(sf.Left(sOccupation, 2)) == "A ")
			{
				sTemp = sf.Right(sOccupation, sf.Len(sOccupation) - 2);
			}

			if (sf.UCase(sf.Left(sOccupation, 3)) == "AN ")
			{
				sTemp = sf.Right(sOccupation, sf.Len(sOccupation) - 3);
			}

			if (sf.UCase(sf.Left(sOccupation, 4)) == "THE ")
			{
				sTemp = sf.Right(sOccupation, sf.Len(sOccupation) - 4);
			}

			//capitalize the first letter of each word in the occupation title
			StringBuilder sb = new StringBuilder(sTemp);

			for (iCnt = 0; iCnt <= sb.Length - 1; iCnt++)
			{
				if (iCnt == 0)
				{
					sb[0] = System.Convert.ToChar(sb[0].ToString().ToUpper());
				}
				else if (sb[iCnt - 1] == ' ')
				{
					sb[iCnt] = System.Convert.ToChar(sb[iCnt].ToString().ToUpper());
				}
			} // iCnt

			sTemp = sb.ToString();

			return sTemp;

		}


		/// <summary>
		/// Summary of GetMoreStats.
		/// </summary>
		/// 		
		private MoreStats GetMoreStats()
		{

			MoreStats moreStats = new MoreStats();
			int NumFamilies = MyFamilies.Count;
			int iFamCnt = 0;
			string FamRefId = "0";
			double childCount = 0;
			Statistics myStatistics;
			DateTime dtActualDate = new DateTime();
			DateTime emptyDate = new DateTime();
			int iYear = 0;
			bool bIsAbout = false;
			int iEstimateStyle = 0;
			int monthNumber;
			int yearNumber;
			int birthYear;
			int deathYear;

			myStatistics = new Statistics();
			for (iFamCnt = 0; iFamCnt <= NumFamilies - 1; iFamCnt++)
			{

				FamRefId = MyFamilies[iFamCnt].ID;
				foreach (Child child in MyChildren)
				{
					if (child.FamilyID == FamRefId)
					{
						childCount += 1;
					}
				} //
				myStatistics.Add(childCount);
			} //
			moreStats.AvgNumChildrenPerFamily = myStatistics.Mean;


			myStatistics = new Statistics();
			foreach (Individual individual in MyIndividuals)
			{
				birthYear = 0;
				deathYear = 0;
				if (individual.BirthDateText.Length != 0)
				{
					Tools.GetDates(individual.BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
					birthYear = iYear;

					if (dtActualDate != emptyDate)
					{
						monthNumber = dtActualDate.Month;
						switch (monthNumber)
						{
							case 1: moreStats.NumBornInJAN += 1; break;
							case 2: moreStats.NumBornInFEB += 1; break;
							case 3: moreStats.NumBornInMAR += 1; break;
							case 4: moreStats.NumBornInAPR += 1; break;
							case 5: moreStats.NumBornInMAY += 1; break;
							case 6: moreStats.NumBornInJUN += 1; break;
							case 7: moreStats.NumBornInJUL += 1; break;
							case 8: moreStats.NumBornInAUG += 1; break;
							case 9: moreStats.NumBornInSEP += 1; break;
							case 10: moreStats.NumBornInOCT += 1; break;
							case 11: moreStats.NumBornInNOV += 1; break;
							case 12: moreStats.NumBornInDEC += 1; break;
						}
					}

					yearNumber = iYear;
					if (yearNumber != 0)
					{

						if (yearNumber < 1600)
						{
							moreStats.CountBornBefore1600 += 1;
						}
						else if (yearNumber < 1700)
						{
							moreStats.CountBorn17thCentury += 1;
						}
						else if (yearNumber < 1800)
						{
							moreStats.CountBorn18thCentury += 1;
						}
						else if (yearNumber < 1900)
						{
							moreStats.CountBorn19thCentury += 1;
						}
						else if (yearNumber < 2000)
						{
							moreStats.CountBorn20thCentury += 1;
						}
					}

				}

				if (individual.DeathDateText.Length != 0)
				{
					Tools.GetDates(individual.DeathDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
					deathYear = iYear;
					if (dtActualDate != emptyDate)
					{
						monthNumber = dtActualDate.Month;
						switch (monthNumber)
						{
							case 1: moreStats.NumDiedInJAN += 1; break;
							case 2: moreStats.NumDiedInFEB += 1; break;
							case 3: moreStats.NumDiedInMAR += 1; break;
							case 4: moreStats.NumDiedInAPR += 1; break;
							case 5: moreStats.NumDiedInMAY += 1; break;
							case 6: moreStats.NumDiedInJUN += 1; break;
							case 7: moreStats.NumDiedInJUL += 1; break;
							case 8: moreStats.NumDiedInAUG += 1; break;
							case 9: moreStats.NumDiedInSEP += 1; break;
							case 10: moreStats.NumDiedInOCT += 1; break;
							case 11: moreStats.NumDiedInNOV += 1; break;
							case 12: moreStats.NumDiedInDEC += 1; break;
						}
					}
				}

				if (birthYear != 0 && deathYear != 0)
				{
					myStatistics.Add((double)deathYear - birthYear);
				}
			} //

			moreStats.AvgAgeAtDeath = myStatistics.Mean;

			return moreStats;
		}


		/// <summary>
		/// Summary of CreateGUIDKey.
		/// </summary>
		/// 		
		private string CreateGUIDKey()
		{
			Guid myGUID = Guid.NewGuid();
			return myGUID.ToString();
		}


		/// <summary>
		/// Summary of GetOtherLink.
		/// </summary>
		private string GetOtherLink(string sThisLink, int iCnt, bool bIsBook)
		{
			string sTemp;
			string sOtherLink;
			string sChHusband;
			string sChWife;
			string sChTitle;

			char QUOTE = '"';

			if (iCnt == 0)
			{
				sTemp = "See ";
			}
			else
			{
				sTemp = " OR ";
			}

			sOtherLink = sThisLink;
			sOtherLink = sf.Right(sOtherLink, sf.Len(sOtherLink) - 2);
			sOtherLink = sf.Left(sOtherLink, sf.Len(sOtherLink) - 1);
			sOtherLink = System.Convert.ToInt32(sOtherLink).ToString("000");
			if (bIsBook == true)
			{
				sOtherLink = "#F-" + sOtherLink;
			}
			else
			{
				sOtherLink = m_cfg.FamFilePrefix + sOtherLink + _pext;
			}

			sTemp = sTemp + "<a href=" + QUOTE + sOtherLink + QUOTE + ">";

			//the husband
			if (MyFamilies[sThisLink].Husband != "?")
			{
				sChHusband = MyIndividuals[MyFamilies[sThisLink].Husband].Forenames + " " + MyIndividuals[MyFamilies[sThisLink].Husband].Surname;
				if (MyIndividuals[MyFamilies[sThisLink].Husband].Suffix != "")
				{
					sChHusband = sChHusband + ", " + MyIndividuals[MyFamilies[sThisLink].Husband].Suffix;
				}
			}
			else
			{
				sChHusband = "?";
			}

			//the wife
			if (MyFamilies[sThisLink].Wife != "?")
			{
				sChWife = MyIndividuals[MyFamilies[sThisLink].Wife].Forenames + " " + MyIndividuals[MyFamilies[sThisLink].Wife].Surname;
				if (MyIndividuals[MyFamilies[sThisLink].Wife].Suffix != "")
				{
					sChWife = sChWife + ", " + MyIndividuals[MyFamilies[sThisLink].Wife].Suffix;
				}
			}
			else
			{
				sChWife = "?";
			}

			//title of web page that we are linking to
			sChTitle = sChHusband + " &amp; " + sChWife;

			sTemp = sTemp + sChTitle;
			sTemp = sTemp + "</a>";
			return sTemp;
		}


		/// <summary>
		/// Summary of GetOtherMarriages.
		/// </summary>
		private ArrayList GetOtherMarriages(string sThisIndId, string sThisFamId)
		{
			int iNumFound = -1;

			const int MAX_NUM_FOUND = 32; //some ridiculous number

			ArrayList aryOtherFams = new ArrayList();

			try
			{
				foreach (Family family in MyFamilies)
				{
					if (iNumFound == MAX_NUM_FOUND - 1) break;
					if (family.Husband == sThisIndId && family.ID != sThisFamId)
					{
						iNumFound++;
						aryOtherFams.Add(family.ID);
					}
					else if (family.Wife == sThisIndId && family.ID != sThisFamId)
					{
						iNumFound++;
						aryOtherFams.Add(family.ID);
					}
				} // family


			}
			catch
			{
				//empty catch
			}

			return aryOtherFams;

		}
		#endregion

		#region Methods For Estimating Dates
		//const for estimating dates
		//private const int MAX_LIVING_AGE = 75;
		//private const int MAX_BAPT_AGE = 10;
		//private const int MAX_BURIAL_ELAPSED = 1;
		//private const int MIN_MARR_AGE = 20;
		//private const int MIN_CHILDBIRTH = 20;
		//private const int MAX_CHILDBIRTH = 50;
		//private const int ABT_EQUALS = 2;
		//private const int MAX_AGE_DIFF = 10;
		//private const int MAX_ITERATIONS = 5;
		//private const int MIN_ALLOWED_YEAR = 0;


		private int _MaxLivingAge = 75;
		private int _MaxBaptismAge = 10;
		private int _MaxBurialElapsed = 1;
		private int _MinMarrAge = 20;
		private int _MinChildBirth = 20;
		private int _MaxChildBirth = 50;
		private int _AboutEquals = 2;
		private int _MaxAgeDifference = 10;
		private int _MaxIterations = 5;
		private int _MinAllowedYear = 0;


		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MinAllowedYear
		{
			get
			{
				return _MinAllowedYear;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MaxLivingAge
		{
			get
			{
				return _MaxLivingAge;
			}
			set
			{
				_MaxLivingAge = value;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MaxBaptismAge
		{
			get
			{
				return _MaxBaptismAge;
			}
			set
			{
				_MaxBaptismAge = value;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MaxBurialElapsed
		{
			get
			{
				return _MaxBurialElapsed;
			}
			set
			{
				_MaxBurialElapsed = value;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MinMarrAge
		{
			get
			{
				return _MinMarrAge;
			}
			set
			{
				_MinMarrAge = value;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MinChildBirth
		{
			get
			{
				return _MinChildBirth;
			}
			set
			{
				_MinChildBirth = value;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MaxChildBirth
		{
			get
			{
				return _MaxChildBirth;
			}
			set
			{
				_MaxChildBirth = value;
			}
		}

		/// <summary>
		/// Defines the range in years of ABT
		/// </summary>
		public int AboutEquals
		{
			get
			{
				return _AboutEquals;
			}
			set
			{
				_AboutEquals = value;
			}
		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MaxAgeDifference
		{
			get
			{
				return _MaxAgeDifference;
			}
			set
			{
				_MaxAgeDifference = value;
			}

		}

		/// <summary>
		/// A setting for the date estimation tool.
		/// </summary>
		public int MaxIterations
		{
			get
			{
				return _MaxIterations;
			}
			set
			{
				_MaxIterations = value;
			}
		}

		/// <summary>
		/// Method for used to estimate the birth dates for individuals who are missing birth dates.
		/// </summary>
		/// <remarks>Date estimation is an experimental feature that when turned-on attempts to estimate birth dates for individuals for whom the birh-date is missing. Results may vary depending on how much other date information is available. To find out which individual's dates have been estimated see the file EstimatedDates.txt which is written to the HTML path directory after UncleGED has finished processing the GEDCOM file. If you do not agree the results, you may either turn-off this feature or try adjusting the settings below.</remarks>
		public void EstimateBirthDates()
		{
			DateTime dtActualDate = new DateTime();
			int iYear = 0;
			bool bIsAbout = false;
			int iEstimateStyle = 0;
			string sFamKey;
			int iCnt;
			int iLoopCnt;

			FireStatusMessage("Performing date estimation.");
			for (iLoopCnt = 1; iLoopCnt <= _MaxIterations; iLoopCnt++)
			{
				FireDebugMessage("}: " + iLoopCnt.ToString());
				foreach (Individual individual in MyIndividuals)
				{
					if (individual.BirthDateText.Length == 0 || individual.DidEstimate == true)
					{
						//minBirthYear = 0
						//maxBirthYear = 9999

						Tools.GetDates(individual.BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);

						//do we have any dates for this indi?
						Tools.GetDates(individual.BaptismDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
						if (iYear != 0)
						{
							individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MaxBaptismAge);
							individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear);
							FireDebugMessage("Constraint 1:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
							individual.DidEstimate = true;
						}

						Tools.GetDates(individual.DeathDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
						if (iYear != 0)
						{
							individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MaxLivingAge);
							individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear);
							FireDebugMessage("Constraint 2:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
							individual.DidEstimate = true;
						}

						Tools.GetDates(individual.BurialDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
						if (iYear != 0)
						{
							individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MaxLivingAge);
							individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear);
							FireDebugMessage("Constraint 3:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
							individual.DidEstimate = true;
						}

						foreach (LifeEvent theLifeEvent in individual.LifeEvents)
						{
							Tools.GetDates(theLifeEvent.EventDateString, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
							if (iYear != 0)
							{
								if (iYear < individual.MinBirthYear)
								{
									individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear);
									FireDebugMessage("Constraint 4:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
									individual.DidEstimate = true;
								}
								if (iYear > individual.MaxBirthYear)
								{
									individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear);
									FireDebugMessage("Constraint 5:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
									individual.DidEstimate = true;
								}
							}
						} //

						//get dates for any spouses
						if (individual.FamList.Count > 0)
						{
							for (iCnt = 0; iCnt <= individual.FamList.Count - 1; iCnt++)
							{

								//marriage date
								Tools.GetDates(MyFamilies[individual.FamList[iCnt].FamId].MarriageDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
								if (iYear != 0)
								{
									if (individual.MinBirthYear > iYear)
									{
										individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MinMarrAge);
										FireDebugMessage("Constraint 6:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
										individual.DidEstimate = true;
									}
									if (individual.MaxBirthYear > iYear)
									{
										individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear - _MinMarrAge);
										FireDebugMessage("Constraint 6a:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
										individual.DidEstimate = true;
									}
								}

								//the spouses birth date
								iYear = 0;

								if (individual.Sex == "F")
								{
									if (MyFamilies[individual.FamList[iCnt].FamId].Husband != "?")
									{
										Tools.GetDates(MyIndividuals[MyFamilies[individual.FamList[iCnt].FamId].Husband].BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									}
								}
								else
								{
									if (MyFamilies[individual.FamList[iCnt].FamId].Wife != "?")
									{
										Tools.GetDates(MyIndividuals[MyFamilies[individual.FamList[iCnt].FamId].Wife].BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									}
								}
								if (iYear != 0)
								{
									if (individual.MinBirthYear > iYear)
									{
										individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MinMarrAge);
										FireDebugMessage("Constraint 7:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
										individual.DidEstimate = true;
									}
									if (individual.MaxBirthYear > iYear)
									{
										individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear + _MaxAgeDifference);
										individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MaxAgeDifference);
										FireDebugMessage("Constraint 8:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
										individual.DidEstimate = true;
									}

									//the spouse//s death date
									if (individual.Sex == "F")
									{
										Tools.GetDates(MyIndividuals[MyFamilies[individual.FamList[iCnt].FamId].Husband].DeathDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									}
									else
									{
										Tools.GetDates(MyIndividuals[MyFamilies[individual.FamList[iCnt].FamId].Wife].DeathDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									}
									if (iYear != 0)
									{
										if (individual.MaxBirthYear > iYear)
										{
											individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear - _MinMarrAge);
											individual.DidEstimate = true;
											FireDebugMessage("Constraint 8a:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
										}
									}

								}

								//do the children here....
								//now examine the children//s birth dates
								foreach (Child ch in MyChildren)
								{
									if (ch.FamilyID == individual.FamList[iCnt].FamId)
									{
										Tools.GetDates(MyIndividuals[ch.ChildID].BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
										if (iYear != 0)
										{
											if (individual.MinBirthYear > iYear)
											{
												individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MinChildBirth);
												FireDebugMessage("Constraint 14:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
												individual.DidEstimate = true;
											}
											if (individual.MinBirthYear < iYear - _MaxLivingAge)
											{
												individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear - _MinChildBirth);
												FireDebugMessage("Constraint 14a:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
												individual.DidEstimate = true;
											}
											if (individual.MaxBirthYear > iYear - _MinMarrAge)
											{
												individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear - _MinChildBirth);
												FireDebugMessage("Constraint 15:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
												individual.DidEstimate = true;
											}
											if (individual.MaxBirthYear < iYear - _MaxLivingAge)
											{
												individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear - _MinChildBirth);
												FireDebugMessage("Constraint 15a:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
												individual.DidEstimate = true;
											}
										}
									}
								} //

							} //
						}

						//get dates for parents
						Child child;
						if (individual.FamilyID.Length != 0)
						{
							sFamKey = individual.FamilyID + individual.ID;

							try
							{
								child = MyChildren[sFamKey];
							}
							catch
							{
								child = null;
							}

							if (child != null)
							{
								if (MyFamilies[child.FamilyID].Husband != "?")
								{
									//find dates regarding father
									Tools.GetDates(MyIndividuals[MyFamilies[child.FamilyID].Husband].BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									if (iYear != 0)
									{
										if (individual.MinBirthYear < iYear)
										{
											//adjust based on father//s birth year
											individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear + _MinMarrAge);
											FireDebugMessage("Constraint 9:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
											individual.DidEstimate = true;
										}
									}
									Tools.GetDates(MyIndividuals[MyFamilies[child.FamilyID].Husband].DeathDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									if (iYear != 0)
									{
										if (individual.MaxBirthYear > iYear)
										{
											individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear - 1);
											FireDebugMessage("Constraint 10:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
											individual.DidEstimate = true;
										}
									}
								}
								if (MyFamilies[child.FamilyID].Wife != "?")
								{
									//find dates regarding mother
									Tools.GetDates(MyIndividuals[MyFamilies[child.FamilyID].Wife].BirthDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									if (iYear != 0)
									{
										if (individual.MinBirthYear < iYear)
										{
											//adjust based on mother//s birth year
											individual.MinBirthYear = SetMinYear(individual.MinBirthYear, iYear + _MinChildBirth);
											FireDebugMessage("Constraint 11:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
											individual.DidEstimate = true;
										}
										if (individual.MaxBirthYear > iYear + _MaxChildBirth)
										{
											//adjust based on mother//s birth year
											individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear + _MaxChildBirth);
											FireDebugMessage("Constraint 12:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
											individual.DidEstimate = true;
										}
									}
									Tools.GetDates(MyIndividuals[MyFamilies[child.FamilyID].Wife].DeathDateText, ref dtActualDate, ref iYear, ref bIsAbout, ref iEstimateStyle);
									if (iYear != 0)
									{
										if (individual.MaxBirthYear > iYear)
										{
											individual.MaxBirthYear = SetMaxYear(individual.MaxBirthYear, iYear);
											FireDebugMessage("Constraint 13:" + iYear.ToString() + ":" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
											individual.DidEstimate = true;
										}
									}
								}
							}
						}
						else
						{
							child = null;
						}

						if (individual.MinBirthYear >= _MinAllowedYear)
						{
							if (individual.MinBirthYear > individual.MaxBirthYear)
							{
								individual.MaxBirthYear = individual.MinBirthYear;
								FireDebugMessage("Min > Max:" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
								individual.DidEstimate = true;
							}
							if (individual.MinBirthYear > DateTime.Now.Year)
							{
								individual.MinBirthYear = DateTime.Now.Year;
								FireDebugMessage("Min > Now:" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
								individual.DidEstimate = true;
							}
							if (individual.MaxBirthYear > DateTime.Now.Year)
							{
								individual.MaxBirthYear = DateTime.Now.Year;
								FireDebugMessage("Max > Now:" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
								individual.DidEstimate = true;
							}
							if (individual.MinBirthYear == individual.MaxBirthYear)
							{
								individual.BirthDateText = individual.MinBirthYear.ToString() + " (calc)";
								FireDebugMessage("Min = Max:" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
								individual.DidEstimate = true;
							}
							else
							{
								individual.BirthDateText = "between " + individual.MinBirthYear.ToString() + " and " + individual.MaxBirthYear.ToString() + " (calc)";
								FireDebugMessage("Did Estimate:" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
								individual.DidEstimate = true;
							}
						}
					}
					if (individual.MinBirthYear < _MinAllowedYear)
					{
						individual.MinBirthYear = _MinAllowedYear;
						FireDebugMessage("Min < _MinAllowedYear:" + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
						individual.DidEstimate = true;
					}
					if (individual.MaxBirthYear < 0)
					{
						individual.MaxBirthYear = _MinAllowedYear;
						FireDebugMessage("Max < _MinAllowedYear " + individual.MinBirthYear.ToString() + ":" + individual.MaxBirthYear + " " + individual.ID + ":" + individual.Forenames + " " + individual.Surname);
						individual.DidEstimate = true;
					}

				} //
				Console.Write(".");
			} //
			Console.WriteLine("");

		}

		/// <summary>
		/// Excludes (marks as private) individuals born after <see cref="UncleGED.Application.UGedProject.BirthCutOffYears"/> years.
		/// </summary>
		/// <remarks>This method should only be called after the <see cref="MyIndividuals"/> collection has been built by the <see cref="ProcessGedComFile"/> method.</remarks>
		public void ExcludeLivingIndividuals()
		{

			string sFamKey;
			int j;
			int i;
			Tools ugt = new Tools();
			Individual individual;

			int cutoffYear = DateTime.Now.Year - m_cfg.BirthCutOffYears;
			int birthYear;
			string sBirthYear;

			//_logger.DebugFormat("ExcludeLivingIndividuals MyIndividuals.Count:={0}", MyIndividuals.Count);

			for (i = MyIndividuals.Count - 1; i >= 0; i--)
			{
				individual = MyIndividuals[i];

				try
				{
					if (individual.BirthDateText.Length >= 4)
					{
						sBirthYear = individual.BirthDateText.Trim();
						sBirthYear = sBirthYear.Substring(sBirthYear.Length - 4, 4);
						//_logger.DebugFormat("ExcludeLivingIndividuals ID:={0} BirthDateText:=[{1}] sBirthYear:=[{2}]", individual.ID, individual.BirthDateText, sBirthYear);

						try
						{
							birthYear = System.Int32.Parse(sBirthYear);
						}
						catch
						{

							continue; //if the year is not valid then we can't handle it
						}

						if (birthYear > cutoffYear)
						{
							//_logger.DebugFormat("ExcludeLivingIndividuals ID:={0} BirthDateText:={1} DeathDateText:={2}", individual.ID, individual.BirthDateText, individual.DeathDateText);

							if (individual.FamList.Count > 0)
							{
								for (j = 0; j < individual.FamList.Count; j++)
								{
									if (individual.Sex == "F")
									{
										MyFamilies[individual.FamList[j].FamId].Wife = "?";
									}
									else
									{
										MyFamilies[individual.FamList[j].FamId].Husband = "?";
									}
								}
							}

							sFamKey = individual.FamilyID + individual.ID;
							try
							{
								//_logger.DebugFormat("Removing child {0}", sFamKey);
								MyChildren.Remove(sFamKey);
							}
							catch (Exception ex)
							{
								_logger.DebugFormat("Error: {0}", ex.ToString());
							}
							try
							{
								//_logger.DebugFormat("Removing individual {0}", individual.ID);
								MyIndividuals.Remove(individual.ID);
							}
							catch (Exception ex)
							{
								_logger.DebugFormat("Error: {0}", ex.ToString());
							}
						}
					}

				}
				catch (Exception ex)
				{
					_logger.DebugFormat("Error: {0}", ex.ToString());
				}
			} //

			Family family;
			//now eliminate families where both parents are unknown
			for (i = MyFamilies.Count - 1; i >= 0; i--)
			{
				family = MyFamilies[i];

				if (family.Wife == "?" && family.Husband == "?")
				{
					MyFamilies.Remove(family.ID);
				}
			}
		}

		/// <summary>
		/// Summary of SaveEstimatedDatesList.
		/// </summary>
		/// 		
		public void SaveEstimatedDatesList()
		{
			FileStream fs1;
			StreamWriter sw1;
			string sFile;
			string estDateString;
			string tmgDateString;
			int numInList = 0;

			sFile = m_cfg.HtmlPath + "\\EstimatedDates.txt";
			fs1 = new FileStream(sFile, FileMode.Create);
			sw1 = new StreamWriter(fs1);

			sw1.WriteLine("ID" + '\t' + "NAME" + '\t' + "EST DATE");

			foreach (Individual theIndividual in MyIndividuals)
			{
				if (theIndividual.DidEstimate == true)
				{
					if (theIndividual.MinBirthYear != 0)
					{
						if (theIndividual.MinBirthYear == theIndividual.MaxBirthYear)
						{
							estDateString = "EST " + theIndividual.MinBirthYear.ToString();
							tmgDateString = "1" + theIndividual.MinBirthYear.ToString() + "0000010000000000";
						}
						else
						{
							estDateString = "BET " + theIndividual.MinBirthYear.ToString() + " AND " + theIndividual.MaxBirthYear.ToString();
							tmgDateString = "1" + theIndividual.MinBirthYear.ToString() + "000005" + theIndividual.MaxBirthYear.ToString() + "000000";
						}
						sw1.WriteLine(FixRin(theIndividual.ID) + '\t' + theIndividual.Forenames + " " + theIndividual.Surname + '\t' + estDateString);

						numInList += 1;
					}
				}
			} //

			//sw1.WriteLine(string.Format("List Total: {0}", numInList));

			sw1.Close();
			fs1.Close();
		}

		/// <summary>
		/// Summary of SetMinYear.
		/// </summary>
		private int SetMinYear(int oldMinYear, int newMinYear)
		{
			if (newMinYear < oldMinYear)
			{
				return oldMinYear;
			}
			else
			{
				return newMinYear;
			}
		}

		/// <summary>
		/// Summary of SetMaxYear.
		/// </summary>
		private int SetMaxYear(int oldMaxYear, int newMaxYear)
		{
			if (newMaxYear > oldMaxYear)
			{
				return oldMaxYear;
			}
			else
			{
				return newMaxYear;
			}
		}
		#endregion

		private string _cssFile;
	}

}
